@xen-orchestra/rest-api 0.15.0 → 0.16.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/dist/abstract-classes/base-controller.mjs +4 -2
- package/dist/abstract-classes/xapi-xo-controller.mjs +2 -6
- package/dist/alarms/alarm.service.mjs +2 -1
- package/dist/backup-jobs/backup-job.controller.mjs +284 -0
- package/dist/backup-jobs/backup-job.service.mjs +25 -0
- package/dist/backup-jobs/backup-job.type.mjs +1 -0
- package/dist/backup-logs/backup-log.controller.mjs +75 -0
- package/dist/backup-logs/backup-log.service.mjs +5 -0
- package/dist/groups/group.controller.mjs +37 -1
- package/dist/ioc/ioc.mjs +36 -0
- package/dist/open-api/common/response.common.mjs +4 -0
- package/dist/open-api/oa-examples/backup-job.oa-example.mjs +128 -0
- package/dist/open-api/oa-examples/backup-log.oa-example.mjs +93 -0
- package/dist/open-api/oa-examples/proxy.oa-example.mjs +25 -0
- package/dist/open-api/oa-examples/restore-log.oa-example.mjs +53 -0
- package/dist/open-api/oa-examples/user.oa-example.mjs +32 -0
- package/dist/open-api/oa-examples/vdi.oa-example.mjs +3 -0
- package/dist/open-api/routes/routes.js +1955 -513
- package/dist/proxies/proxy.controller.mjs +61 -0
- package/dist/rest-api/rest-api.mjs +6 -2
- package/dist/restore-logs/restore-log.controller.mjs +144 -0
- package/dist/srs/sr.controller.mjs +39 -2
- package/dist/tasks/task.controller.mjs +49 -3
- package/dist/tasks/task.service.mjs +24 -0
- package/dist/users/user.controller.mjs +91 -12
- package/dist/users/user.service.mjs +21 -0
- package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +46 -4
- package/dist/vdis/vdi.controller.mjs +43 -4
- package/dist/vdis/vdi.service.mjs +21 -0
- package/dist/vm-snapshots/vm-snapshot.controller.mjs +72 -3
- package/dist/vm-templates/vm-template.controller.mjs +43 -2
- package/dist/vms/vm.controller.mjs +135 -5
- package/dist/vms/vm.service.mjs +18 -0
- package/open-api/spec/swagger.json +8373 -4057
- package/package.json +4 -4
- package/tsoa.json +15 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
|
|
11
|
+
import { provide } from 'inversify-binding-decorators';
|
|
12
|
+
import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
13
|
+
import { partialProxies, proxy, proxyIds } from '../open-api/oa-examples/proxy.oa-example.mjs';
|
|
14
|
+
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
15
|
+
let ProxyController = class ProxyController extends XoController {
|
|
16
|
+
getAllCollectionObjects() {
|
|
17
|
+
return this.restApi.xoApp.getAllProxies();
|
|
18
|
+
}
|
|
19
|
+
getCollectionObject(id) {
|
|
20
|
+
return this.restApi.xoApp.getProxy(id);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @example fields "vmUuid,id,name"
|
|
24
|
+
* @example filter "vmUuid?"
|
|
25
|
+
* @example limit 42
|
|
26
|
+
*/
|
|
27
|
+
async getProxies(req, fields, ndjson, filter, limit) {
|
|
28
|
+
const proxies = Object.values(await this.getObjects({ filter, limit }));
|
|
29
|
+
return this.sendObjects(proxies, req);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* @example id "e625ea0c-a876-405a-b838-109d762efe88"
|
|
33
|
+
*/
|
|
34
|
+
getProxy(id) {
|
|
35
|
+
return this.getObject(id);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
__decorate([
|
|
39
|
+
Example(proxyIds),
|
|
40
|
+
Example(partialProxies),
|
|
41
|
+
Get(''),
|
|
42
|
+
__param(0, Request()),
|
|
43
|
+
__param(1, Query()),
|
|
44
|
+
__param(2, Query()),
|
|
45
|
+
__param(3, Query()),
|
|
46
|
+
__param(4, Query())
|
|
47
|
+
], ProxyController.prototype, "getProxies", null);
|
|
48
|
+
__decorate([
|
|
49
|
+
Example(proxy),
|
|
50
|
+
Get('{id}'),
|
|
51
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
52
|
+
__param(0, Path())
|
|
53
|
+
], ProxyController.prototype, "getProxy", null);
|
|
54
|
+
ProxyController = __decorate([
|
|
55
|
+
Route('proxies'),
|
|
56
|
+
Security('*'),
|
|
57
|
+
Response(unauthorizedResp.status, unauthorizedResp.description),
|
|
58
|
+
Tags('proxies'),
|
|
59
|
+
provide(ProxyController)
|
|
60
|
+
], ProxyController);
|
|
61
|
+
export { ProxyController };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as CM from 'complex-matcher';
|
|
1
2
|
import { createLogger } from '@xen-orchestra/log';
|
|
2
3
|
import { invalidCredentials } from 'xo-common/api-errors.js';
|
|
3
4
|
const log = createLogger('xo:rest-api:error-handler');
|
|
@@ -31,8 +32,11 @@ export class RestApi {
|
|
|
31
32
|
getObject(id, type) {
|
|
32
33
|
return this.#xoApp.getObject(id, type);
|
|
33
34
|
}
|
|
34
|
-
getObjectsByType(type, opts) {
|
|
35
|
-
|
|
35
|
+
getObjectsByType(type, { filter, ...opts } = {}) {
|
|
36
|
+
if (filter !== undefined && typeof filter === 'string') {
|
|
37
|
+
filter = CM.parse(filter).createPredicate();
|
|
38
|
+
}
|
|
39
|
+
return this.#xoApp.getObjectsByType(type, { filter, ...opts });
|
|
36
40
|
}
|
|
37
41
|
getXapiObject(maybeId, type) {
|
|
38
42
|
return this.#xoApp.getXapiObject(maybeId, type);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { createLogger } from '@xen-orchestra/log';
|
|
11
|
+
import { Deprecated, Example, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
|
|
12
|
+
import { inject } from 'inversify';
|
|
13
|
+
import { noSuchObject } from 'xo-common/api-errors.js';
|
|
14
|
+
import { provide } from 'inversify-binding-decorators';
|
|
15
|
+
import { BackupLogService } from '../backup-logs/backup-log.service.mjs';
|
|
16
|
+
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
17
|
+
import { unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
18
|
+
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
19
|
+
import { partialRestoreLogs, restoreLog, restoreLogIds } from '../open-api/oa-examples/restore-log.oa-example.mjs';
|
|
20
|
+
const log = createLogger('xo:rest-api:restoreLog-controller');
|
|
21
|
+
let RestoreLogController = class RestoreLogController extends XoController {
|
|
22
|
+
#backupLogService;
|
|
23
|
+
constructor(restApi, backupLogService) {
|
|
24
|
+
super(restApi);
|
|
25
|
+
this.#backupLogService = backupLogService;
|
|
26
|
+
}
|
|
27
|
+
getAllCollectionObjects() {
|
|
28
|
+
const filter = log => !this.#backupLogService.isBackupLog(log);
|
|
29
|
+
return this.restApi.xoApp.getBackupNgLogsSorted({ filter });
|
|
30
|
+
}
|
|
31
|
+
async getCollectionObject(id) {
|
|
32
|
+
const log = await this.restApi.xoApp.getBackupNgLogs(id);
|
|
33
|
+
if (this.#backupLogService.isBackupLog(log)) {
|
|
34
|
+
throw noSuchObject('restore-log');
|
|
35
|
+
}
|
|
36
|
+
return log;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @example fields "jobName,status,data"
|
|
40
|
+
* @example filter "status:success"
|
|
41
|
+
* @example limit 42
|
|
42
|
+
*/
|
|
43
|
+
async getRestoreLogs(req, fields, ndjson, filter, limit) {
|
|
44
|
+
const restoreLogs = await this.getObjects({ filter, limit });
|
|
45
|
+
return this.sendObjects(Object.values(restoreLogs), req);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @example id "fo"
|
|
49
|
+
*/
|
|
50
|
+
getRestoreLog(id) {
|
|
51
|
+
return this.getObject(id);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
__decorate([
|
|
55
|
+
Example(restoreLogIds),
|
|
56
|
+
Example(partialRestoreLogs),
|
|
57
|
+
Get(''),
|
|
58
|
+
__param(0, Request()),
|
|
59
|
+
__param(1, Query()),
|
|
60
|
+
__param(2, Query()),
|
|
61
|
+
__param(3, Query()),
|
|
62
|
+
__param(4, Query())
|
|
63
|
+
], RestoreLogController.prototype, "getRestoreLogs", null);
|
|
64
|
+
__decorate([
|
|
65
|
+
Example(restoreLog),
|
|
66
|
+
Get('{id}'),
|
|
67
|
+
__param(0, Path())
|
|
68
|
+
], RestoreLogController.prototype, "getRestoreLog", null);
|
|
69
|
+
RestoreLogController = __decorate([
|
|
70
|
+
Route('restore-logs'),
|
|
71
|
+
Security('*'),
|
|
72
|
+
Response(unauthorizedResp.status, unauthorizedResp.description),
|
|
73
|
+
Tags('restore-logs'),
|
|
74
|
+
provide(RestoreLogController),
|
|
75
|
+
__param(0, inject(RestApi)),
|
|
76
|
+
__param(1, inject(BackupLogService))
|
|
77
|
+
], RestoreLogController);
|
|
78
|
+
export { RestoreLogController };
|
|
79
|
+
// ----------- DEPRECATED TO BE REMOVED IN ONE YEAR (09-12-2026)--------------------
|
|
80
|
+
let DeprecatedRestoreController = class DeprecatedRestoreController extends XoController {
|
|
81
|
+
#backupLogService;
|
|
82
|
+
constructor(restApi, backupLogService) {
|
|
83
|
+
super(restApi);
|
|
84
|
+
this.#backupLogService = backupLogService;
|
|
85
|
+
}
|
|
86
|
+
getAllCollectionObjects() {
|
|
87
|
+
const filter = log => !this.#backupLogService.isBackupLog(log);
|
|
88
|
+
return this.restApi.xoApp.getBackupNgLogsSorted({ filter });
|
|
89
|
+
}
|
|
90
|
+
async getCollectionObject(id) {
|
|
91
|
+
const log = await this.restApi.xoApp.getBackupNgLogs(id);
|
|
92
|
+
if (this.#backupLogService.isBackupLog(log)) {
|
|
93
|
+
throw noSuchObject('restore-log');
|
|
94
|
+
}
|
|
95
|
+
return log;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* @example fields "jobName,status,data"
|
|
99
|
+
* @example filter "status:success"
|
|
100
|
+
* @example limit 42
|
|
101
|
+
*/
|
|
102
|
+
async getDeprecatedRestoreLogs(req, fields, ndjson, filter, limit) {
|
|
103
|
+
const restoreLogs = await this.getObjects({ filter, limit });
|
|
104
|
+
return this.sendObjects(Object.values(restoreLogs), req, 'restore-logs');
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* @example id "1758180544428"
|
|
108
|
+
*/
|
|
109
|
+
getDeprecatedRestoreLog(id) {
|
|
110
|
+
return this.getObject(id);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
__decorate([
|
|
114
|
+
Example(restoreLogIds),
|
|
115
|
+
Example(partialRestoreLogs),
|
|
116
|
+
Deprecated(),
|
|
117
|
+
Get(''),
|
|
118
|
+
__param(0, Request()),
|
|
119
|
+
__param(1, Query()),
|
|
120
|
+
__param(2, Query()),
|
|
121
|
+
__param(3, Query()),
|
|
122
|
+
__param(4, Query())
|
|
123
|
+
], DeprecatedRestoreController.prototype, "getDeprecatedRestoreLogs", null);
|
|
124
|
+
__decorate([
|
|
125
|
+
Example(restoreLog),
|
|
126
|
+
Deprecated(),
|
|
127
|
+
Get('{id}'),
|
|
128
|
+
__param(0, Path())
|
|
129
|
+
], DeprecatedRestoreController.prototype, "getDeprecatedRestoreLog", null);
|
|
130
|
+
DeprecatedRestoreController = __decorate([
|
|
131
|
+
Route('restore/logs'),
|
|
132
|
+
Security('*'),
|
|
133
|
+
Response(unauthorizedResp.status, unauthorizedResp.description),
|
|
134
|
+
Middlewares((_req, _res, next) => {
|
|
135
|
+
log.warn('You are calling a deprecated route. It will be removed in the futur. Please use `/rest/v0/restore-logs` instead');
|
|
136
|
+
next();
|
|
137
|
+
}),
|
|
138
|
+
Tags('restore-logs'),
|
|
139
|
+
provide(DeprecatedRestoreController),
|
|
140
|
+
__param(0, inject(RestApi)),
|
|
141
|
+
__param(1, inject(BackupLogService))
|
|
142
|
+
], DeprecatedRestoreController);
|
|
143
|
+
export { DeprecatedRestoreController };
|
|
144
|
+
// ----------- DEPRECATED TO BE REMOVED IN ONE YEAR (09-12-2026)--------------------
|
|
@@ -7,14 +7,17 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
|
|
10
|
+
import { Example, Get, Path, Post, Query, Request, Response, Route, Security, SuccessResponse, Tags } from 'tsoa';
|
|
11
11
|
import { inject } from 'inversify';
|
|
12
12
|
import { provide } from 'inversify-binding-decorators';
|
|
13
|
+
import { SUPPORTED_VDI_FORMAT } from '@vates/types';
|
|
13
14
|
import { AlarmService } from '../alarms/alarm.service.mjs';
|
|
15
|
+
import { BASE_URL } from '../index.mjs';
|
|
14
16
|
import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
|
|
15
17
|
import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
|
|
16
|
-
import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
18
|
+
import { createdResp, notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
17
19
|
import { partialSrs, sr, srIds } from '../open-api/oa-examples/sr.oa-example.mjs';
|
|
20
|
+
import { vdiId } from '../open-api/oa-examples/vdi.oa-example.mjs';
|
|
18
21
|
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
19
22
|
import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
|
|
20
23
|
let SrController = class SrController extends XapiXoController {
|
|
@@ -51,6 +54,28 @@ let SrController = class SrController extends XapiXoController {
|
|
|
51
54
|
});
|
|
52
55
|
return this.sendObjects(Object.values(alarms), req, 'alarms');
|
|
53
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Import an exported VDI
|
|
59
|
+
* @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
|
|
60
|
+
* @example name_label "VDI_foo_import"
|
|
61
|
+
* @example name_description "VDI imported by the REST API"
|
|
62
|
+
* @example raw true
|
|
63
|
+
*/
|
|
64
|
+
async srImportVdi(req, id, name_label, name_description, raw) {
|
|
65
|
+
const xapiSr = this.getXapiObject(id);
|
|
66
|
+
const xapi = xapiSr.$xapi;
|
|
67
|
+
if (req.headers['content-length'] !== undefined) {
|
|
68
|
+
req.length = +req.headers['content-length'];
|
|
69
|
+
}
|
|
70
|
+
const vdiRef = await xapi.SR_importVdi(xapiSr.$ref, req, {
|
|
71
|
+
format: raw ? SUPPORTED_VDI_FORMAT.raw : SUPPORTED_VDI_FORMAT.vhd,
|
|
72
|
+
name_label,
|
|
73
|
+
name_description,
|
|
74
|
+
});
|
|
75
|
+
const vdiId = await xapi.getField('VDI', vdiRef, 'uuid');
|
|
76
|
+
this.setHeader('Location', `${BASE_URL}/vdis/${vdiId}`);
|
|
77
|
+
return { id: vdiId };
|
|
78
|
+
}
|
|
54
79
|
};
|
|
55
80
|
__decorate([
|
|
56
81
|
Example(srIds),
|
|
@@ -80,6 +105,18 @@ __decorate([
|
|
|
80
105
|
__param(4, Query()),
|
|
81
106
|
__param(5, Query())
|
|
82
107
|
], SrController.prototype, "getSrAlarms", null);
|
|
108
|
+
__decorate([
|
|
109
|
+
Example(vdiId),
|
|
110
|
+
Post('{id}/vdis'),
|
|
111
|
+
Tags('vdis'),
|
|
112
|
+
SuccessResponse(createdResp.status, 'VDI imported'),
|
|
113
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
114
|
+
__param(0, Request()),
|
|
115
|
+
__param(1, Path()),
|
|
116
|
+
__param(2, Query()),
|
|
117
|
+
__param(3, Query()),
|
|
118
|
+
__param(4, Query())
|
|
119
|
+
], SrController.prototype, "srImportVdi", null);
|
|
83
120
|
SrController = __decorate([
|
|
84
121
|
Route('srs'),
|
|
85
122
|
Security('*'),
|
|
@@ -9,10 +9,10 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
9
9
|
};
|
|
10
10
|
import * as CM from 'complex-matcher';
|
|
11
11
|
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
12
|
-
import { Get, Path, Query, Request, Route, Security, Tags, Response, Example } from 'tsoa';
|
|
13
|
-
import { badRequestResp, notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
12
|
+
import { Get, Path, Query, Request, Route, Security, Tags, Response, Example, Delete, Post, SuccessResponse } from 'tsoa';
|
|
13
|
+
import { asynchronousActionResp, badRequestResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
14
14
|
import { provide } from 'inversify-binding-decorators';
|
|
15
|
-
import { partialTasks, task, taskIds } from '../open-api/oa-examples/task.oa-example.mjs';
|
|
15
|
+
import { partialTasks, task, taskIds, taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
|
|
16
16
|
import pDefer from 'promise-toolbox/defer';
|
|
17
17
|
import { ApiError } from '../helpers/error.helper.mjs';
|
|
18
18
|
import { Transform } from 'node:stream';
|
|
@@ -90,6 +90,33 @@ let TaskController = class TaskController extends XoController {
|
|
|
90
90
|
}
|
|
91
91
|
return this.getObject(taskId);
|
|
92
92
|
}
|
|
93
|
+
async deleteTasks() {
|
|
94
|
+
await this.restApi.tasks.clearLogs();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* @example id "0mdd1basu"
|
|
98
|
+
*/
|
|
99
|
+
async deleteTask(id) {
|
|
100
|
+
const task = await this.getObject(id);
|
|
101
|
+
await this.restApi.tasks.deleteLog(task.id);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @example id "0mdd1basu"
|
|
105
|
+
*/
|
|
106
|
+
async abortTask(id, sync) {
|
|
107
|
+
const taskId = id;
|
|
108
|
+
const action = async () => {
|
|
109
|
+
await this.restApi.tasks.abort(taskId);
|
|
110
|
+
};
|
|
111
|
+
return this.createAction(action, {
|
|
112
|
+
sync,
|
|
113
|
+
statusCode: noContentResp.status,
|
|
114
|
+
taskProperties: {
|
|
115
|
+
name: 'abort task',
|
|
116
|
+
objectId: taskId,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
93
120
|
};
|
|
94
121
|
__decorate([
|
|
95
122
|
Example(taskIds),
|
|
@@ -111,6 +138,25 @@ __decorate([
|
|
|
111
138
|
__param(1, Path()),
|
|
112
139
|
__param(2, Query())
|
|
113
140
|
], TaskController.prototype, "getTask", null);
|
|
141
|
+
__decorate([
|
|
142
|
+
Delete(''),
|
|
143
|
+
SuccessResponse(noContentResp.status, noContentResp.description)
|
|
144
|
+
], TaskController.prototype, "deleteTasks", null);
|
|
145
|
+
__decorate([
|
|
146
|
+
Delete('{id}'),
|
|
147
|
+
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
148
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
149
|
+
__param(0, Path())
|
|
150
|
+
], TaskController.prototype, "deleteTask", null);
|
|
151
|
+
__decorate([
|
|
152
|
+
Example(taskLocation),
|
|
153
|
+
Post('{id}/actions/abort'),
|
|
154
|
+
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description, asynchronousActionResp.produce),
|
|
155
|
+
Response(noContentResp.status, noContentResp.description),
|
|
156
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
157
|
+
__param(0, Path()),
|
|
158
|
+
__param(1, Query())
|
|
159
|
+
], TaskController.prototype, "abortTask", null);
|
|
114
160
|
TaskController = __decorate([
|
|
115
161
|
Route('tasks'),
|
|
116
162
|
Security('*'),
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as CM from 'complex-matcher';
|
|
2
|
+
export class TaskService {
|
|
3
|
+
#restApi;
|
|
4
|
+
constructor(restApi) {
|
|
5
|
+
this.#restApi = restApi;
|
|
6
|
+
}
|
|
7
|
+
async getTasks({ filter, limit = Infinity } = {}) {
|
|
8
|
+
const tasks = [];
|
|
9
|
+
let userFilter = () => true;
|
|
10
|
+
if (filter !== undefined) {
|
|
11
|
+
userFilter = typeof filter === 'string' ? CM.parse(filter).createPredicate() : filter;
|
|
12
|
+
}
|
|
13
|
+
for await (const task of this.#restApi.tasks.list()) {
|
|
14
|
+
if (limit === 0) {
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
if (userFilter(task)) {
|
|
18
|
+
tasks.push(task);
|
|
19
|
+
limit--;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return tasks;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -8,28 +8,35 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { Body, Delete, Example, Get, Middlewares, Patch, Path, Post, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
|
|
11
|
+
import { inject } from 'inversify';
|
|
11
12
|
import { json } from 'express';
|
|
12
13
|
import { provide } from 'inversify-binding-decorators';
|
|
13
14
|
import { createdResp, forbiddenOperationResp, invalidParameters, noContentResp, notFoundResp, resourceAlreadyExists, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
14
15
|
import { forbiddenOperation } from 'xo-common/api-errors.js';
|
|
15
|
-
import { partialUsers, user, userId, userIds } from '../open-api/oa-examples/user.oa-example.mjs';
|
|
16
|
+
import { partialUsers, user, authenticationTokens, userId, userIds } from '../open-api/oa-examples/user.oa-example.mjs';
|
|
17
|
+
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
18
|
+
import { limitAndFilterArray } from '../helpers/utils.helper.mjs';
|
|
19
|
+
import { UserService } from './user.service.mjs';
|
|
16
20
|
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
21
|
+
import { groupIds, partialGroups } from '../open-api/oa-examples/group.oa-example.mjs';
|
|
17
22
|
let UserController = class UserController extends XoController {
|
|
23
|
+
#userService;
|
|
24
|
+
constructor(restApi, userService) {
|
|
25
|
+
super(restApi);
|
|
26
|
+
this.#userService = userService;
|
|
27
|
+
}
|
|
18
28
|
// --- abstract methods
|
|
19
29
|
async getAllCollectionObjects() {
|
|
20
|
-
|
|
21
|
-
return users.map(user => this.#sanitizeUser(user));
|
|
30
|
+
return this.#userService.getUsers();
|
|
22
31
|
}
|
|
23
32
|
async getCollectionObject(id) {
|
|
24
|
-
|
|
25
|
-
return this.#sanitizeUser(user);
|
|
33
|
+
return this.#userService.getUser(id);
|
|
26
34
|
}
|
|
27
|
-
#
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
return sanitizedUser;
|
|
35
|
+
#redirectCurrentUser(req) {
|
|
36
|
+
const currentUser = this.restApi.getCurrentUser();
|
|
37
|
+
const originalUrl = req.originalUrl;
|
|
38
|
+
const res = req.res;
|
|
39
|
+
res.redirect(307, originalUrl.replace(/\/users\/me(?=\/|$)/, `/users/${currentUser.id}`));
|
|
33
40
|
}
|
|
34
41
|
/**
|
|
35
42
|
* @example fields "permission,name,id"
|
|
@@ -40,6 +47,18 @@ let UserController = class UserController extends XoController {
|
|
|
40
47
|
const users = Object.values(await this.getObjects({ filter, limit }));
|
|
41
48
|
return this.sendObjects(users, req);
|
|
42
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Redirect to `/users/:id`
|
|
52
|
+
*/
|
|
53
|
+
redirectMe(req) {
|
|
54
|
+
this.#redirectCurrentUser(req);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Redirect to `/users/:id/<rest-of-your-path>`
|
|
58
|
+
*/
|
|
59
|
+
redirectMeWithPath(req) {
|
|
60
|
+
this.#redirectCurrentUser(req);
|
|
61
|
+
}
|
|
43
62
|
/**
|
|
44
63
|
* @example id "722d17b9-699b-49d2-8193-be1ac573d3de"
|
|
45
64
|
*/
|
|
@@ -87,6 +106,31 @@ let UserController = class UserController extends XoController {
|
|
|
87
106
|
async deleteUser(id) {
|
|
88
107
|
await this.restApi.xoApp.deleteUser(id);
|
|
89
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* @example id "722d17b9-699b-49d2-8193-be1ac573d3de"
|
|
111
|
+
* @example fields "name,id,users"
|
|
112
|
+
* @example filter "users:length:>0"
|
|
113
|
+
* @example limit 42
|
|
114
|
+
*/
|
|
115
|
+
async getUserGroups(req, id, fields, ndjson, filter, limit) {
|
|
116
|
+
const user = await this.getObject(id);
|
|
117
|
+
const groups = await Promise.all(user.groups.map(group => this.restApi.xoApp.getGroup(group)));
|
|
118
|
+
return this.sendObjects(limitAndFilterArray(groups, { filter, limit }), req, 'groups');
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* @example id "722d17b9-699b-49d2-8193-be1ac573d3de"
|
|
122
|
+
* @example filter "expiration:>1757371582496"
|
|
123
|
+
* @example limit 42
|
|
124
|
+
*/
|
|
125
|
+
async getAuthenticationTokens(req, id, filter, limit) {
|
|
126
|
+
const user = await this.getObject(id);
|
|
127
|
+
const me = this.restApi.getCurrentUser();
|
|
128
|
+
if (me.id !== user.id) {
|
|
129
|
+
throw forbiddenOperation('get authentication tokens', 'can only see own authentication tokens');
|
|
130
|
+
}
|
|
131
|
+
const tokens = await this.restApi.xoApp.getAuthenticationTokensForUser(user.id);
|
|
132
|
+
return limitAndFilterArray(tokens, { filter, limit });
|
|
133
|
+
}
|
|
90
134
|
};
|
|
91
135
|
__decorate([
|
|
92
136
|
Example(userIds),
|
|
@@ -98,6 +142,16 @@ __decorate([
|
|
|
98
142
|
__param(3, Query()),
|
|
99
143
|
__param(4, Query())
|
|
100
144
|
], UserController.prototype, "getUsers", null);
|
|
145
|
+
__decorate([
|
|
146
|
+
SuccessResponse(307, 'Temporary redirect'),
|
|
147
|
+
Get('me'),
|
|
148
|
+
__param(0, Request())
|
|
149
|
+
], UserController.prototype, "redirectMe", null);
|
|
150
|
+
__decorate([
|
|
151
|
+
SuccessResponse(307, 'Temporary redirect'),
|
|
152
|
+
Get('me/*'),
|
|
153
|
+
__param(0, Request())
|
|
154
|
+
], UserController.prototype, "redirectMeWithPath", null);
|
|
101
155
|
__decorate([
|
|
102
156
|
Example(user),
|
|
103
157
|
Get('{id}'),
|
|
@@ -129,11 +183,36 @@ __decorate([
|
|
|
129
183
|
Response(notFoundResp.status, notFoundResp.description),
|
|
130
184
|
__param(0, Path())
|
|
131
185
|
], UserController.prototype, "deleteUser", null);
|
|
186
|
+
__decorate([
|
|
187
|
+
Example(groupIds),
|
|
188
|
+
Example(partialGroups),
|
|
189
|
+
Get('{id}/groups'),
|
|
190
|
+
Tags('groups'),
|
|
191
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
192
|
+
__param(0, Request()),
|
|
193
|
+
__param(1, Path()),
|
|
194
|
+
__param(2, Query()),
|
|
195
|
+
__param(3, Query()),
|
|
196
|
+
__param(4, Query()),
|
|
197
|
+
__param(5, Query())
|
|
198
|
+
], UserController.prototype, "getUserGroups", null);
|
|
199
|
+
__decorate([
|
|
200
|
+
Example(authenticationTokens),
|
|
201
|
+
Get('{id}/authentication_tokens'),
|
|
202
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
203
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
204
|
+
__param(0, Request()),
|
|
205
|
+
__param(1, Path()),
|
|
206
|
+
__param(2, Query()),
|
|
207
|
+
__param(3, Query())
|
|
208
|
+
], UserController.prototype, "getAuthenticationTokens", null);
|
|
132
209
|
UserController = __decorate([
|
|
133
210
|
Route('users'),
|
|
134
211
|
Security('*'),
|
|
135
212
|
Response(unauthorizedResp.status, unauthorizedResp.description),
|
|
136
213
|
Tags('users'),
|
|
137
|
-
provide(UserController)
|
|
214
|
+
provide(UserController),
|
|
215
|
+
__param(0, inject(RestApi)),
|
|
216
|
+
__param(1, inject(UserService))
|
|
138
217
|
], UserController);
|
|
139
218
|
export { UserController };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class UserService {
|
|
2
|
+
#restApi;
|
|
3
|
+
constructor(restApi) {
|
|
4
|
+
this.#restApi = restApi;
|
|
5
|
+
}
|
|
6
|
+
#sanitizeUser(user) {
|
|
7
|
+
const sanitizedUser = { ...user };
|
|
8
|
+
if (sanitizedUser.pw_hash !== undefined) {
|
|
9
|
+
sanitizedUser.pw_hash = '***obfuscated***';
|
|
10
|
+
}
|
|
11
|
+
return sanitizedUser;
|
|
12
|
+
}
|
|
13
|
+
async getUser(id) {
|
|
14
|
+
const user = await this.#restApi.xoApp.getUser(id);
|
|
15
|
+
return this.#sanitizeUser(user);
|
|
16
|
+
}
|
|
17
|
+
async getUsers() {
|
|
18
|
+
const users = await this.#restApi.xoApp.getAllUsers();
|
|
19
|
+
return users.map(user => this.#sanitizeUser(user));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -7,21 +7,24 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
|
|
10
|
+
import { Delete, Example, Get, Path, Query, Request, Response, Route, Security, SuccessResponse, Tags } from 'tsoa';
|
|
11
11
|
import { inject } from 'inversify';
|
|
12
12
|
import { provide } from 'inversify-binding-decorators';
|
|
13
13
|
import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
|
|
14
|
-
import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
14
|
+
import { noContentResp, notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
15
15
|
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
16
16
|
import { partialVdiSnapshots, vdiSnapshot, vdiSnapshotIds } from '../open-api/oa-examples/vdi-snapshot.oa-example.mjs';
|
|
17
17
|
import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
|
|
18
18
|
import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
|
|
19
19
|
import { AlarmService } from '../alarms/alarm.service.mjs';
|
|
20
|
+
import { VdiService } from '../vdis/vdi.service.mjs';
|
|
20
21
|
let VdiSnapshotController = class VdiSnapshotController extends XapiXoController {
|
|
21
22
|
#alarmService;
|
|
22
|
-
|
|
23
|
+
#vdiService;
|
|
24
|
+
constructor(restApi, alarmService, vdiService) {
|
|
23
25
|
super('VDI-snapshot', restApi);
|
|
24
26
|
this.#alarmService = alarmService;
|
|
27
|
+
this.#vdiService = vdiService;
|
|
25
28
|
}
|
|
26
29
|
/**
|
|
27
30
|
* @example fields "uuid,snapshot_time,$snapshot_of"
|
|
@@ -31,6 +34,22 @@ let VdiSnapshotController = class VdiSnapshotController extends XapiXoController
|
|
|
31
34
|
getVdiSnapshots(req, fields, ndjson, filter, limit) {
|
|
32
35
|
return this.sendObjects(Object.values(this.getObjects({ filter, limit })), req);
|
|
33
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
* Export VDI-snapshot content
|
|
40
|
+
*
|
|
41
|
+
* @example id "d2727772-735b-478f-b6f9-11e7db56dfd0"
|
|
42
|
+
*/
|
|
43
|
+
async exportVdiSnapshotContent(req, id, format) {
|
|
44
|
+
const res = req.res;
|
|
45
|
+
const stream = await this.#vdiService.exportContent(id, 'VDI-snapshot', {
|
|
46
|
+
format,
|
|
47
|
+
response: res,
|
|
48
|
+
});
|
|
49
|
+
process.on('SIGTERM', () => req.destroy());
|
|
50
|
+
req.on('close', () => stream.destroy());
|
|
51
|
+
return stream;
|
|
52
|
+
}
|
|
34
53
|
/**
|
|
35
54
|
* @example id "d2727772-735b-478f-b6f9-11e7db56dfd0"
|
|
36
55
|
*/
|
|
@@ -51,6 +70,13 @@ let VdiSnapshotController = class VdiSnapshotController extends XapiXoController
|
|
|
51
70
|
});
|
|
52
71
|
return this.sendObjects(Object.values(alarms), req, 'alarms');
|
|
53
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* @example id "d2727772-735b-478f-b6f9-11e7db56dfd0"
|
|
75
|
+
*/
|
|
76
|
+
async deleteVdiSnapshot(id) {
|
|
77
|
+
const xapiVdiSnapshot = this.getXapiObject(id);
|
|
78
|
+
await xapiVdiSnapshot.$xapi.VDI_destroy(xapiVdiSnapshot.$ref);
|
|
79
|
+
}
|
|
54
80
|
};
|
|
55
81
|
__decorate([
|
|
56
82
|
Example(vdiSnapshotIds),
|
|
@@ -62,6 +88,15 @@ __decorate([
|
|
|
62
88
|
__param(3, Query()),
|
|
63
89
|
__param(4, Query())
|
|
64
90
|
], VdiSnapshotController.prototype, "getVdiSnapshots", null);
|
|
91
|
+
__decorate([
|
|
92
|
+
Get('{id}.{format}'),
|
|
93
|
+
SuccessResponse(200, 'Download started', 'application/octet-stream'),
|
|
94
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
95
|
+
Response(422, 'Invalid format'),
|
|
96
|
+
__param(0, Request()),
|
|
97
|
+
__param(1, Path()),
|
|
98
|
+
__param(2, Path())
|
|
99
|
+
], VdiSnapshotController.prototype, "exportVdiSnapshotContent", null);
|
|
65
100
|
__decorate([
|
|
66
101
|
Example(vdiSnapshot),
|
|
67
102
|
Get('{id}'),
|
|
@@ -80,6 +115,12 @@ __decorate([
|
|
|
80
115
|
__param(4, Query()),
|
|
81
116
|
__param(5, Query())
|
|
82
117
|
], VdiSnapshotController.prototype, "getVdiSnapshotAlarms", null);
|
|
118
|
+
__decorate([
|
|
119
|
+
Delete('{id}'),
|
|
120
|
+
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
121
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
122
|
+
__param(0, Path())
|
|
123
|
+
], VdiSnapshotController.prototype, "deleteVdiSnapshot", null);
|
|
83
124
|
VdiSnapshotController = __decorate([
|
|
84
125
|
Route('vdi-snapshots'),
|
|
85
126
|
Security('*'),
|
|
@@ -87,6 +128,7 @@ VdiSnapshotController = __decorate([
|
|
|
87
128
|
Tags('vdis'),
|
|
88
129
|
provide(VdiSnapshotController),
|
|
89
130
|
__param(0, inject(RestApi)),
|
|
90
|
-
__param(1, inject(AlarmService))
|
|
131
|
+
__param(1, inject(AlarmService)),
|
|
132
|
+
__param(2, inject(VdiService))
|
|
91
133
|
], VdiSnapshotController);
|
|
92
134
|
export { VdiSnapshotController };
|