@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.
Files changed (36) hide show
  1. package/dist/abstract-classes/base-controller.mjs +4 -2
  2. package/dist/abstract-classes/xapi-xo-controller.mjs +2 -6
  3. package/dist/alarms/alarm.service.mjs +2 -1
  4. package/dist/backup-jobs/backup-job.controller.mjs +284 -0
  5. package/dist/backup-jobs/backup-job.service.mjs +25 -0
  6. package/dist/backup-jobs/backup-job.type.mjs +1 -0
  7. package/dist/backup-logs/backup-log.controller.mjs +75 -0
  8. package/dist/backup-logs/backup-log.service.mjs +5 -0
  9. package/dist/groups/group.controller.mjs +37 -1
  10. package/dist/ioc/ioc.mjs +36 -0
  11. package/dist/open-api/common/response.common.mjs +4 -0
  12. package/dist/open-api/oa-examples/backup-job.oa-example.mjs +128 -0
  13. package/dist/open-api/oa-examples/backup-log.oa-example.mjs +93 -0
  14. package/dist/open-api/oa-examples/proxy.oa-example.mjs +25 -0
  15. package/dist/open-api/oa-examples/restore-log.oa-example.mjs +53 -0
  16. package/dist/open-api/oa-examples/user.oa-example.mjs +32 -0
  17. package/dist/open-api/oa-examples/vdi.oa-example.mjs +3 -0
  18. package/dist/open-api/routes/routes.js +1955 -513
  19. package/dist/proxies/proxy.controller.mjs +61 -0
  20. package/dist/rest-api/rest-api.mjs +6 -2
  21. package/dist/restore-logs/restore-log.controller.mjs +144 -0
  22. package/dist/srs/sr.controller.mjs +39 -2
  23. package/dist/tasks/task.controller.mjs +49 -3
  24. package/dist/tasks/task.service.mjs +24 -0
  25. package/dist/users/user.controller.mjs +91 -12
  26. package/dist/users/user.service.mjs +21 -0
  27. package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +46 -4
  28. package/dist/vdis/vdi.controller.mjs +43 -4
  29. package/dist/vdis/vdi.service.mjs +21 -0
  30. package/dist/vm-snapshots/vm-snapshot.controller.mjs +72 -3
  31. package/dist/vm-templates/vm-template.controller.mjs +43 -2
  32. package/dist/vms/vm.controller.mjs +135 -5
  33. package/dist/vms/vm.service.mjs +18 -0
  34. package/open-api/spec/swagger.json +8373 -4057
  35. package/package.json +4 -4
  36. 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
- return this.#xoApp.getObjectsByType(type, opts);
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
- const users = await this.restApi.xoApp.getAllUsers();
21
- return users.map(user => this.#sanitizeUser(user));
30
+ return this.#userService.getUsers();
22
31
  }
23
32
  async getCollectionObject(id) {
24
- const user = await this.restApi.xoApp.getUser(id);
25
- return this.#sanitizeUser(user);
33
+ return this.#userService.getUser(id);
26
34
  }
27
- #sanitizeUser(user) {
28
- const sanitizedUser = { ...user };
29
- if (sanitizedUser.pw_hash !== undefined) {
30
- sanitizedUser.pw_hash = '***obfuscated***';
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
- constructor(restApi, alarmService) {
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 };