@kapeta/local-cluster-service 0.12.1 → 0.14.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/index.js +4 -0
  3. package/dist/cjs/src/api.d.ts +3 -0
  4. package/dist/cjs/src/api.js +22 -0
  5. package/dist/cjs/src/assetManager.d.ts +3 -1
  6. package/dist/cjs/src/assetManager.js +20 -4
  7. package/dist/cjs/src/assets/routes.js +23 -2
  8. package/dist/cjs/src/containerManager.js +130 -109
  9. package/dist/cjs/src/instanceManager.d.ts +4 -3
  10. package/dist/cjs/src/instanceManager.js +80 -59
  11. package/dist/cjs/src/instances/routes.js +19 -11
  12. package/dist/cjs/src/operatorManager.d.ts +5 -3
  13. package/dist/cjs/src/operatorManager.js +34 -23
  14. package/dist/cjs/src/providerManager.js +1 -1
  15. package/dist/cjs/src/repositoryManager.d.ts +2 -4
  16. package/dist/cjs/src/repositoryManager.js +51 -66
  17. package/dist/cjs/src/socketManager.js +1 -1
  18. package/dist/cjs/src/taskManager.d.ts +64 -0
  19. package/dist/cjs/src/taskManager.js +161 -0
  20. package/dist/cjs/src/tasks/routes.d.ts +3 -0
  21. package/dist/cjs/src/tasks/routes.js +35 -0
  22. package/dist/esm/index.js +4 -0
  23. package/dist/esm/src/api.d.ts +3 -0
  24. package/dist/esm/src/api.js +17 -0
  25. package/dist/esm/src/assetManager.d.ts +3 -1
  26. package/dist/esm/src/assetManager.js +20 -4
  27. package/dist/esm/src/assets/routes.js +23 -2
  28. package/dist/esm/src/containerManager.js +130 -109
  29. package/dist/esm/src/instanceManager.d.ts +4 -3
  30. package/dist/esm/src/instanceManager.js +80 -59
  31. package/dist/esm/src/instances/routes.js +19 -11
  32. package/dist/esm/src/operatorManager.d.ts +5 -3
  33. package/dist/esm/src/operatorManager.js +34 -23
  34. package/dist/esm/src/providerManager.js +1 -1
  35. package/dist/esm/src/repositoryManager.d.ts +2 -4
  36. package/dist/esm/src/repositoryManager.js +51 -66
  37. package/dist/esm/src/socketManager.js +1 -1
  38. package/dist/esm/src/taskManager.d.ts +64 -0
  39. package/dist/esm/src/taskManager.js +157 -0
  40. package/dist/esm/src/tasks/routes.d.ts +3 -0
  41. package/dist/esm/src/tasks/routes.js +30 -0
  42. package/index.ts +9 -0
  43. package/package.json +2 -1
  44. package/src/api.ts +21 -0
  45. package/src/assetManager.ts +28 -4
  46. package/src/assets/routes.ts +24 -2
  47. package/src/containerManager.ts +151 -126
  48. package/src/instanceManager.ts +106 -70
  49. package/src/instances/routes.ts +18 -12
  50. package/src/operatorManager.ts +46 -28
  51. package/src/providerManager.ts +1 -1
  52. package/src/repositoryManager.ts +65 -63
  53. package/src/socketManager.ts +1 -1
  54. package/src/taskManager.ts +223 -0
  55. package/src/tasks/routes.ts +38 -0
  56. package/src/utils/BlockInstanceRunner.ts +0 -2
@@ -10,6 +10,7 @@ const cors_1 = require("../middleware/cors");
10
10
  const kapeta_1 = require("../middleware/kapeta");
11
11
  const stringBody_1 = require("../middleware/stringBody");
12
12
  const types_1 = require("../types");
13
+ const taskManager_1 = require("../taskManager");
13
14
  const router = (0, express_promise_router_1.default)();
14
15
  router.use('/', cors_1.corsHandler);
15
16
  router.use('/', kapeta_1.kapetaHeaders);
@@ -29,33 +30,40 @@ router.get('/:systemId/instances', (req, res) => {
29
30
  * Start all instances in a plan
30
31
  */
31
32
  router.post('/:systemId/start', async (req, res) => {
32
- const instances = await instanceManager_1.instanceManager.startAllForPlan(req.params.systemId);
33
+ const task = await instanceManager_1.instanceManager.startAllForPlan(req.params.systemId);
33
34
  res.status(202).send({
34
35
  ok: true,
35
- processes: instances.map((p) => {
36
- return { pid: p.pid, type: p.type };
37
- }),
36
+ taskId: task.id,
38
37
  });
39
38
  });
40
39
  /**
41
40
  * Stop all instances in plan
42
41
  */
43
42
  router.post('/:systemId/stop', async (req, res) => {
44
- await instanceManager_1.instanceManager.stopAllForPlan(req.params.systemId);
43
+ const task = instanceManager_1.instanceManager.stopAllForPlan(req.params.systemId);
45
44
  res.status(202).send({
46
45
  ok: true,
46
+ taskId: task.id,
47
47
  });
48
48
  });
49
49
  /**
50
50
  * Start single instance in a plan
51
51
  */
52
52
  router.post('/:systemId/:instanceId/start', async (req, res) => {
53
- const process = await instanceManager_1.instanceManager.start(req.params.systemId, req.params.instanceId);
54
- res.status(202).send({
55
- ok: true,
56
- pid: process.pid,
57
- type: process.type,
58
- });
53
+ const result = await instanceManager_1.instanceManager.start(req.params.systemId, req.params.instanceId);
54
+ if (result instanceof taskManager_1.Task) {
55
+ res.status(202).send({
56
+ ok: true,
57
+ taskId: result.id,
58
+ });
59
+ }
60
+ else {
61
+ res.status(202).send({
62
+ ok: true,
63
+ pid: result.pid,
64
+ type: result.type,
65
+ });
66
+ }
59
67
  });
60
68
  /**
61
69
  * Stop single instance in a plan
@@ -1,10 +1,12 @@
1
+ import { DefinitionInfo } from '@kapeta/local-cluster-config';
1
2
  import { ContainerInfo } from './containerManager';
2
3
  import { EnvironmentType, OperatorInfo } from './types';
3
4
  export declare const KIND_OPERATOR = "core/resource-type-operator";
4
5
  declare class Operator {
5
- private _data;
6
- constructor(data: any);
7
- getData(): any;
6
+ private readonly _data;
7
+ constructor(data: DefinitionInfo);
8
+ getLocalData(): any;
9
+ getDefinitionInfo(): DefinitionInfo;
8
10
  getCredentials(): any;
9
11
  }
10
12
  declare class OperatorManager {
@@ -15,17 +15,21 @@ const definitionsManager_1 = require("./definitionsManager");
15
15
  const utils_1 = require("./utils/utils");
16
16
  const lodash_1 = __importDefault(require("lodash"));
17
17
  const async_lock_1 = __importDefault(require("async-lock"));
18
+ const taskManager_1 = require("./taskManager");
18
19
  exports.KIND_OPERATOR = 'core/resource-type-operator';
19
20
  class Operator {
20
21
  _data;
21
22
  constructor(data) {
22
23
  this._data = data;
23
24
  }
24
- getData() {
25
+ getLocalData() {
26
+ return this._data.definition.spec.local;
27
+ }
28
+ getDefinitionInfo() {
25
29
  return this._data;
26
30
  }
27
31
  getCredentials() {
28
- return this._data.credentials;
32
+ return this._data.definition.spec.local.credentials;
29
33
  }
30
34
  }
31
35
  class OperatorManager {
@@ -58,7 +62,7 @@ class OperatorManager {
58
62
  if (!operator.definition.spec || !operator.definition.spec.local) {
59
63
  throw new Error(`Operator missing local definition: ${resourceType}:${version}`);
60
64
  }
61
- return new Operator(operator.definition.spec.local);
65
+ return new Operator(operator);
62
66
  }
63
67
  /**
64
68
  * Get information about a specific consumed resource
@@ -119,7 +123,7 @@ class OperatorManager {
119
123
  const key = `${systemId}#${resourceType}:${version}`;
120
124
  return await this.operatorLock.acquire(key, async () => {
121
125
  const operator = this.getOperator(resourceType, version);
122
- const operatorData = operator.getData();
126
+ const operatorData = operator.getLocalData();
123
127
  const portTypes = Object.keys(operatorData.ports);
124
128
  portTypes.sort();
125
129
  const ports = {};
@@ -147,6 +151,7 @@ class OperatorManager {
147
151
  const Labels = {
148
152
  kapeta: 'true',
149
153
  };
154
+ const operatorMetadata = operator.getDefinitionInfo().definition.metadata;
150
155
  const bindHost = (0, utils_1.getBindHost)();
151
156
  const ExposedPorts = {};
152
157
  lodash_1.default.forEach(ports, (portInfo, containerPort) => {
@@ -163,26 +168,32 @@ class OperatorManager {
163
168
  lodash_1.default.forEach(operatorData.env, (value, name) => {
164
169
  Env.push(name + '=' + value);
165
170
  });
166
- let HealthCheck = undefined;
167
- if (operatorData.health) {
168
- HealthCheck = containerManager_1.containerManager.toDockerHealth(operatorData.health);
169
- }
170
- const container = await containerManager_1.containerManager.ensureContainer({
171
- name: containerName,
172
- Image: operatorData.image,
173
- Hostname: containerName + '.kapeta',
174
- Labels,
175
- Cmd: operatorData.cmd,
176
- ExposedPorts,
177
- Env,
178
- HealthCheck,
179
- HostConfig: {
180
- PortBindings,
181
- Mounts,
182
- },
171
+ const task = taskManager_1.taskManager.add(`operator:ensure:${key}`, async () => {
172
+ let HealthCheck = undefined;
173
+ if (operatorData.health) {
174
+ HealthCheck = containerManager_1.containerManager.toDockerHealth(operatorData.health);
175
+ }
176
+ const container = await containerManager_1.containerManager.ensureContainer({
177
+ name: containerName,
178
+ Image: operatorData.image,
179
+ Hostname: containerName + '.kapeta',
180
+ Labels,
181
+ Cmd: operatorData.cmd,
182
+ ExposedPorts,
183
+ Env,
184
+ HealthCheck,
185
+ HostConfig: {
186
+ PortBindings,
187
+ Mounts,
188
+ },
189
+ });
190
+ await containerManager_1.containerManager.waitForReady(container);
191
+ return new containerManager_1.ContainerInfo(container);
192
+ }, {
193
+ name: `Ensuring ${operatorMetadata.title ?? operatorMetadata.name}`,
194
+ systemId,
183
195
  });
184
- await containerManager_1.containerManager.waitForReady(container);
185
- return new containerManager_1.ContainerInfo(container);
196
+ return task.wait();
186
197
  });
187
198
  }
188
199
  }
@@ -22,7 +22,7 @@ class ProviderManager {
22
22
  if (this._webAssetCache[id] && (await fs_extra_1.default.pathExists(this._webAssetCache[id]))) {
23
23
  return fs_extra_1.default.readFile(this._webAssetCache[id], 'utf8');
24
24
  }
25
- await repositoryManager_1.repositoryManager.ensureAsset(handle, name, version);
25
+ await repositoryManager_1.repositoryManager.ensureAsset(handle, name, version, true);
26
26
  const installedProvider = this.getWebProviders().find((providerDefinition) => {
27
27
  return providerDefinition.definition.metadata.name === fullName && providerDefinition.version === version;
28
28
  });
@@ -1,17 +1,15 @@
1
+ import { Task } from './taskManager';
1
2
  declare class RepositoryManager {
2
3
  private changeEventsEnabled;
3
4
  private _registryService;
4
5
  private _cache;
5
6
  private watcher?;
6
- private _installQueue;
7
- private _processing;
8
7
  constructor();
9
8
  setChangeEventsEnabled(enabled: boolean): void;
10
9
  listenForChanges(): void;
11
10
  stopListening(): void;
12
11
  private _install;
13
- _processNext(): Promise<void>;
14
- ensureAsset(handle: string, name: string, version: string): Promise<void>;
12
+ ensureAsset(handle: string, name: string, version: string, wait?: boolean): Promise<undefined | Task[]>;
15
13
  }
16
14
  export declare const repositoryManager: RepositoryManager;
17
15
  export {};
@@ -15,20 +15,20 @@ const socketManager_1 = require("./socketManager");
15
15
  const progressListener_1 = require("./progressListener");
16
16
  const nodejs_registry_utils_1 = require("@kapeta/nodejs-registry-utils");
17
17
  const definitionsManager_1 = require("./definitionsManager");
18
+ const taskManager_1 = require("./taskManager");
19
+ const utils_1 = require("./utils/utils");
20
+ const assetManager_1 = require("./assetManager");
18
21
  const INSTALL_ATTEMPTED = {};
19
22
  class RepositoryManager {
20
23
  changeEventsEnabled;
21
24
  _registryService;
22
25
  _cache;
23
26
  watcher;
24
- _installQueue;
25
- _processing = false;
26
27
  constructor() {
27
28
  this.changeEventsEnabled = true;
28
29
  this.listenForChanges();
29
30
  this._registryService = new nodejs_registry_utils_1.RegistryService(nodejs_registry_utils_1.Config.data.registry.url);
30
31
  this._cache = {};
31
- this._installQueue = [];
32
32
  }
33
33
  setChangeEventsEnabled(enabled) {
34
34
  this.changeEventsEnabled = enabled;
@@ -109,74 +109,54 @@ class RepositoryManager {
109
109
  }
110
110
  async _install(refs) {
111
111
  //We make sure to only install one asset at a time - otherwise unexpected things might happen
112
- const out = new Promise((resolve, reject) => {
113
- this._installQueue.push(async () => {
114
- try {
115
- const normalizedRefs = refs.map((ref) => (0, nodejs_utils_1.parseKapetaUri)(ref).id);
116
- const filteredRefs = normalizedRefs
117
- .filter((ref) => !INSTALL_ATTEMPTED[ref])
118
- .filter((ref) => !definitionsManager_1.definitionsManager.exists(ref));
119
- if (filteredRefs.length > 0) {
120
- console.log(`Auto-installing dependencies: ${filteredRefs.join(', ')}`);
121
- filteredRefs.forEach((ref) => (INSTALL_ATTEMPTED[ref] = true));
122
- //Auto-install missing asset
123
- try {
124
- //We change to a temp dir to avoid issues with the current working directory
125
- process.chdir(node_os_1.default.tmpdir());
126
- //Disable change events while installing
127
- this.setChangeEventsEnabled(false);
128
- socketManager_1.socketManager.emit(`install`, 'install:action', {
129
- type: 'start',
130
- refs,
131
- });
132
- await nodejs_registry_utils_1.Actions.install(progressListener_1.progressListener, normalizedRefs, {});
133
- socketManager_1.socketManager.emit(`install`, 'install:action', {
134
- type: 'done',
135
- refs,
136
- });
137
- }
138
- catch (e) {
139
- socketManager_1.socketManager.emit(`install`, 'install:action', {
140
- type: 'failed',
141
- refs,
142
- error: e.message,
143
- });
144
- }
145
- finally {
146
- this.setChangeEventsEnabled(true);
147
- }
148
- }
149
- resolve();
112
+ const createInstaller = (ref) => {
113
+ return async () => {
114
+ if (INSTALL_ATTEMPTED[ref]) {
115
+ return;
150
116
  }
151
- catch (e) {
152
- reject(e);
117
+ if (definitionsManager_1.definitionsManager.exists(ref)) {
118
+ return;
153
119
  }
154
- finally {
155
- this._processNext().catch((e) => console.error(e));
120
+ console.log(`Installing asset: ${ref}`);
121
+ INSTALL_ATTEMPTED[ref] = true;
122
+ //Auto-install missing asset
123
+ try {
124
+ //We change to a temp dir to avoid issues with the current working directory
125
+ process.chdir(node_os_1.default.tmpdir());
126
+ //Disable change events while installing
127
+ this.setChangeEventsEnabled(false);
128
+ await nodejs_registry_utils_1.Actions.install(progressListener_1.progressListener, [ref], {});
156
129
  }
157
- });
158
- });
159
- this._processNext().catch((e) => console.error(e));
160
- return out;
161
- }
162
- async _processNext() {
163
- if (this._processing) {
164
- return;
165
- }
166
- this._processing = true;
167
- try {
168
- while (this._installQueue.length > 0) {
169
- const item = this._installQueue.shift();
170
- if (item) {
171
- await item();
130
+ finally {
131
+ this.setChangeEventsEnabled(true);
172
132
  }
133
+ definitionsManager_1.definitionsManager.clearCache();
134
+ assetManager_1.assetManager.clearCache();
135
+ console.log(`Asset installed: ${ref}`);
136
+ };
137
+ };
138
+ const tasks = [];
139
+ while (refs.length > 0) {
140
+ let ref = refs.shift();
141
+ if (!ref) {
142
+ continue;
173
143
  }
144
+ ref = (0, utils_1.normalizeKapetaUri)(ref);
145
+ if (INSTALL_ATTEMPTED[ref]) {
146
+ continue;
147
+ }
148
+ if (definitionsManager_1.definitionsManager.exists(ref)) {
149
+ continue;
150
+ }
151
+ const task = taskManager_1.taskManager.add(`asset:install:${ref}`, createInstaller(ref), {
152
+ name: `Installing ${ref}`,
153
+ group: 'asset:install:',
154
+ });
155
+ tasks.push(task);
174
156
  }
175
- finally {
176
- this._processing = false;
177
- }
157
+ return tasks;
178
158
  }
179
- async ensureAsset(handle, name, version) {
159
+ async ensureAsset(handle, name, version, wait = true) {
180
160
  const fullName = `${handle}/${name}`;
181
161
  const ref = `${fullName}:${version}`;
182
162
  if (version === 'local') {
@@ -207,16 +187,21 @@ class RepositoryManager {
207
187
  throw e;
208
188
  }
209
189
  this._cache[ref] = true;
190
+ let tasks = undefined;
210
191
  if (!installedAsset) {
211
- await this._install([ref]);
192
+ tasks = await this._install([ref]);
212
193
  }
213
194
  else {
214
195
  //Ensure dependencies are installed
215
196
  const refs = assetVersion.dependencies.map((dep) => dep.name);
216
197
  if (refs.length > 0) {
217
- await this._install(refs);
198
+ tasks = await this._install(refs);
218
199
  }
219
200
  }
201
+ if (tasks && wait) {
202
+ await Promise.all(tasks.map((t) => t.future.promise));
203
+ }
204
+ return tasks;
220
205
  }
221
206
  }
222
207
  exports.repositoryManager = new RepositoryManager();
@@ -30,7 +30,7 @@ class SocketManager {
30
30
  this.io.to(context).emit(type, { context, payload });
31
31
  }
32
32
  emitGlobal(type, payload) {
33
- this.io.emit(type, { payload });
33
+ this.io.emit(type, payload);
34
34
  }
35
35
  _bindIO() {
36
36
  this.io.on('connection', (socket) => this._handleSocketCreated(socket));
@@ -0,0 +1,64 @@
1
+ export type TaskRunner<T> = (task: Task<T>) => Promise<T>;
2
+ export declare enum TaskStatus {
3
+ PENDING = "PENDING",
4
+ RUNNING = "RUNNING",
5
+ COMPLETED = "COMPLETED",
6
+ FAILED = "FAILED"
7
+ }
8
+ interface Future<T = void> {
9
+ promise: Promise<T>;
10
+ resolve: (result: T) => void;
11
+ reject: (e: any) => void;
12
+ }
13
+ interface TaskMetadata {
14
+ name: string;
15
+ /**
16
+ * A unique prefix for the task. If defined only 1 task with this ID prefix will be executed at a time
17
+ */
18
+ group?: string;
19
+ progress?: number;
20
+ [key: string]: any;
21
+ }
22
+ interface TaskData<T = void> {
23
+ id: string;
24
+ status: TaskStatus;
25
+ errorMessage?: string;
26
+ metadata: TaskMetadata;
27
+ future: Future<T>;
28
+ run: TaskRunner<T>;
29
+ }
30
+ export declare class Task<T = void> implements TaskData<T> {
31
+ private data;
32
+ constructor(task: TaskData<T>);
33
+ get id(): string;
34
+ get status(): TaskStatus;
35
+ get errorMessage(): string | undefined;
36
+ get metadata(): TaskMetadata;
37
+ get future(): Future<T>;
38
+ get run(): TaskRunner<T>;
39
+ set status(status: TaskStatus);
40
+ set errorMessage(errorMessage: string | undefined);
41
+ set metadata(metadata: TaskMetadata);
42
+ emitUpdate(): void;
43
+ wait(): Promise<T>;
44
+ toData(): {
45
+ id: string;
46
+ status: TaskStatus;
47
+ errorMessage?: string | undefined;
48
+ metadata: TaskMetadata;
49
+ future: Future<T>;
50
+ run: TaskRunner<T>;
51
+ };
52
+ }
53
+ declare class TaskManager {
54
+ private _tasks;
55
+ add<T>(id: string, runner: TaskRunner<T>, metadata: TaskMetadata): Task<T>;
56
+ waitFor(filter: (task: Task<any>) => boolean): Promise<void>;
57
+ get(taskId: string): Task<any> | undefined;
58
+ exists(taskId: string): boolean;
59
+ remove(taskId: string): void;
60
+ list(): TaskData[];
61
+ private invokeTask;
62
+ }
63
+ export declare const taskManager: TaskManager;
64
+ export {};
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.taskManager = exports.Task = exports.TaskStatus = void 0;
4
+ /**
5
+ * Class that handles processing background tasks.
6
+ */
7
+ const socketManager_1 = require("./socketManager");
8
+ const EVENT_TASK_UPDATED = 'task-updated';
9
+ const EVENT_TASK_ADDED = 'task-added';
10
+ const EVENT_TASK_REMOVED = 'task-removed';
11
+ var TaskStatus;
12
+ (function (TaskStatus) {
13
+ TaskStatus["PENDING"] = "PENDING";
14
+ TaskStatus["RUNNING"] = "RUNNING";
15
+ TaskStatus["COMPLETED"] = "COMPLETED";
16
+ TaskStatus["FAILED"] = "FAILED";
17
+ })(TaskStatus || (exports.TaskStatus = TaskStatus = {}));
18
+ class Task {
19
+ data;
20
+ constructor(task) {
21
+ this.data = task;
22
+ }
23
+ get id() {
24
+ return this.data.id;
25
+ }
26
+ get status() {
27
+ return this.data.status;
28
+ }
29
+ get errorMessage() {
30
+ return this.data.errorMessage;
31
+ }
32
+ get metadata() {
33
+ return this.data.metadata;
34
+ }
35
+ get future() {
36
+ return this.data.future;
37
+ }
38
+ get run() {
39
+ return this.data.run;
40
+ }
41
+ set status(status) {
42
+ this.data.status = status;
43
+ }
44
+ set errorMessage(errorMessage) {
45
+ this.data.errorMessage = errorMessage;
46
+ }
47
+ set metadata(metadata) {
48
+ this.data.metadata = metadata;
49
+ }
50
+ emitUpdate() {
51
+ socketManager_1.socketManager.emitGlobal(EVENT_TASK_UPDATED, this.toData());
52
+ }
53
+ async wait() {
54
+ return this.future.promise;
55
+ }
56
+ toData() {
57
+ return { ...this.data };
58
+ }
59
+ }
60
+ exports.Task = Task;
61
+ function createFuture() {
62
+ let resolve = () => { };
63
+ let reject = () => { };
64
+ const promise = new Promise((res, rej) => {
65
+ resolve = res;
66
+ reject = rej;
67
+ });
68
+ return {
69
+ promise,
70
+ resolve,
71
+ reject,
72
+ };
73
+ }
74
+ class TaskManager {
75
+ _tasks = [];
76
+ add(id, runner, metadata) {
77
+ const existingTask = this.get(id);
78
+ if (existingTask) {
79
+ return existingTask;
80
+ }
81
+ const future = createFuture();
82
+ const task = new Task({
83
+ id,
84
+ status: TaskStatus.PENDING,
85
+ metadata,
86
+ future,
87
+ run: runner,
88
+ });
89
+ this._tasks.push(task);
90
+ socketManager_1.socketManager.emitGlobal(EVENT_TASK_ADDED, task.toData());
91
+ this.invokeTask(task).catch(() => { });
92
+ return task;
93
+ }
94
+ async waitFor(filter) {
95
+ const tasks = this._tasks.filter(filter);
96
+ while (tasks.length > 0) {
97
+ const task = tasks.shift();
98
+ if (!task) {
99
+ continue;
100
+ }
101
+ try {
102
+ await task.wait();
103
+ }
104
+ catch (e) {
105
+ //Ignore
106
+ }
107
+ }
108
+ }
109
+ get(taskId) {
110
+ return this._tasks.find((t) => t.id === taskId);
111
+ }
112
+ exists(taskId) {
113
+ return !!this.get(taskId);
114
+ }
115
+ remove(taskId) {
116
+ const task = this.get(taskId);
117
+ if (!task) {
118
+ return;
119
+ }
120
+ if (task.status === TaskStatus.RUNNING) {
121
+ throw new Error('Cannot remove a running task');
122
+ }
123
+ this._tasks = this._tasks.filter((t) => t.id !== taskId);
124
+ socketManager_1.socketManager.emitGlobal(EVENT_TASK_REMOVED, task.toData());
125
+ }
126
+ list() {
127
+ return this._tasks.map((t) => t.toData());
128
+ }
129
+ async invokeTask(task) {
130
+ if (task.metadata.group) {
131
+ const existingTaskInGroup = this._tasks.find((t) => t.id !== task.id && t.metadata.group === task.metadata.group && t.status === TaskStatus.RUNNING);
132
+ if (existingTaskInGroup) {
133
+ return;
134
+ }
135
+ }
136
+ try {
137
+ task.status = TaskStatus.RUNNING;
138
+ task.emitUpdate();
139
+ const result = await task.run(task);
140
+ task.status = TaskStatus.COMPLETED;
141
+ task.future.resolve(result);
142
+ task.emitUpdate();
143
+ }
144
+ catch (e) {
145
+ task.errorMessage = e.message;
146
+ task.status = TaskStatus.FAILED;
147
+ task.future.reject(e);
148
+ task.emitUpdate();
149
+ }
150
+ finally {
151
+ this.remove(task.id);
152
+ }
153
+ if (task.metadata.group) {
154
+ const nextTaskInGroup = this._tasks.find((t) => t.id !== task.id && t.metadata.group === task.metadata.group && t.status === TaskStatus.PENDING);
155
+ if (nextTaskInGroup) {
156
+ return this.invokeTask(nextTaskInGroup);
157
+ }
158
+ }
159
+ }
160
+ }
161
+ exports.taskManager = new TaskManager();
@@ -0,0 +1,3 @@
1
+ /// <reference types="express" />
2
+ declare const router: import("express").Router;
3
+ export default router;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const express_promise_router_1 = __importDefault(require("express-promise-router"));
7
+ const cors_1 = require("../middleware/cors");
8
+ const taskManager_1 = require("../taskManager");
9
+ const router = (0, express_promise_router_1.default)();
10
+ router.use('/', cors_1.corsHandler);
11
+ /**
12
+ * Get all current tasks
13
+ */
14
+ router.get('/', (req, res) => {
15
+ res.send(taskManager_1.taskManager.list());
16
+ });
17
+ router.get('/:taskId', (req, res) => {
18
+ const task = taskManager_1.taskManager.get(req.params.taskId);
19
+ if (!task) {
20
+ res.status(404).send({ error: 'Task not found' });
21
+ return;
22
+ }
23
+ res.send(task.toData());
24
+ });
25
+ router.delete('/:taskId', (req, res) => {
26
+ try {
27
+ taskManager_1.taskManager.remove(req.params.taskId);
28
+ res.send({ ok: true });
29
+ }
30
+ catch (e) {
31
+ res.status(400).send({ error: e.message });
32
+ return;
33
+ }
34
+ });
35
+ exports.default = router;
package/dist/esm/index.js CHANGED
@@ -15,6 +15,8 @@ import FilesystemRoutes from './src/filesystem/routes';
15
15
  import AssetsRoutes from './src/assets/routes';
16
16
  import ProviderRoutes from './src/providers/routes';
17
17
  import AttachmentRoutes from './src/attachments/routes';
18
+ import TaskRoutes from './src/tasks/routes';
19
+ import APIRoutes from './src/api';
18
20
  import { getBindHost } from './src/utils/utils';
19
21
  import request from 'request';
20
22
  let currentServer = null;
@@ -29,6 +31,8 @@ function createServer() {
29
31
  app.use('/assets', AssetsRoutes);
30
32
  app.use('/providers', ProviderRoutes);
31
33
  app.use('/attachments', AttachmentRoutes);
34
+ app.use('/tasks', TaskRoutes);
35
+ app.use('/api', APIRoutes);
32
36
  app.get('/status', async (req, res) => {
33
37
  res.send({
34
38
  ok: true,
@@ -0,0 +1,3 @@
1
+ /// <reference types="express" />
2
+ declare const router: import("express").Router;
3
+ export default router;