@xen-orchestra/rest-api 0.32.0 → 0.34.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/README.md +36 -4
- package/dist/backup-jobs/backup-job.service.mjs +4 -0
- package/dist/backup-repositories/backup-repositories.controller.mjs +115 -6
- package/dist/backup-repositories/backup-repository.service.mjs +46 -0
- package/dist/ioc/ioc.mjs +8 -0
- package/dist/middlewares/acl.middleware.mjs +10 -1
- package/dist/middlewares/deprecated.middleware.mjs +10 -0
- package/dist/middlewares/generic-error-handler.middleware.mjs +1 -0
- package/dist/open-api/oa-examples/backup-repository.oa-example.mjs +1 -0
- package/dist/open-api/routes/routes.js +266 -20
- package/dist/pbds/pbd.controller.mjs +10 -0
- package/dist/pools/pool.controller.mjs +48 -0
- package/dist/srs/sr.controller.mjs +15 -0
- package/dist/vbds/vbd.controller.mjs +20 -1
- package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +27 -2
- package/dist/vdis/vdi.controller.mjs +26 -2
- package/dist/vifs/vif.controller.mjs +79 -6
- package/dist/vm-snapshots/vm-snapshot.controller.mjs +2 -1
- package/dist/vm-templates/vm-template.controller.mjs +2 -1
- package/dist/vms/vm.controller.mjs +70 -5
- package/dist/vms/vm.service.mjs +29 -11
- package/open-api/spec/swagger.json +1707 -68
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -22,6 +22,29 @@ npm install --save @xen-orchestra/rest-api
|
|
|
22
22
|
|
|
23
23
|
The REST API is based on the `TSOA` framework and therefore we use decorators a lot to define the behavior of a route or a group of routes. To keep things easily visible, it is best to always use the decorators in the same order.
|
|
24
24
|
|
|
25
|
+
At the request of the DevOps team, any REST API PR that updates the OpenAPI specification must include a reviewer from the DevOps team. (Non-blocking for merge.)
|
|
26
|
+
|
|
27
|
+
### Resource consistency
|
|
28
|
+
|
|
29
|
+
For a given resource (VMs, VIFs, users, etc.), property names must be consistent across all endpoints.
|
|
30
|
+
|
|
31
|
+
> Example: a PATCH request on a VM must use the same property names as the VM representation returned by the REST API.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
GET /vms/:id
|
|
35
|
+
{
|
|
36
|
+
name_label: 'Foo',
|
|
37
|
+
name_description: 'Foo Bar',
|
|
38
|
+
...
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
PATCH /vms/:id
|
|
42
|
+
{
|
|
43
|
+
name_label: 'Bar' // OK ✅
|
|
44
|
+
nameDescription: 'Bar Foo' // Not OK ❌
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
25
48
|
### Class decorator
|
|
26
49
|
|
|
27
50
|
```ts
|
|
@@ -46,6 +69,7 @@ class Foo extends Controller {
|
|
|
46
69
|
* @example id 1234
|
|
47
70
|
*/
|
|
48
71
|
@Example(['foo', 'bar'])
|
|
72
|
+
@Extension('x-mcp-exposure', 'allow')
|
|
49
73
|
@Get('{id}')
|
|
50
74
|
@Security('*')
|
|
51
75
|
@Middlewares(json())
|
|
@@ -70,7 +94,7 @@ If an endpoint does not have a middleware ACL, it will be accessible **ONLY** to
|
|
|
70
94
|
|
|
71
95
|
It is sometimes necessary to check ACLs based on the body of the request sent by the user (for example, for a PATCH endpoint). For this, you can use `actions` (which allows you to pass multiple actions) and `actionsFromBody` (a function exported from `acl.middleware.mts`).
|
|
72
96
|
|
|
73
|
-
`actionsFromBody(['update:
|
|
97
|
+
`actionsFromBody(['update:nameLabel', 'update:nameDescription'])` checks if `nameLabel` is present in the request body, and then applies the ACL check. The same applies to `nameDescription`.
|
|
74
98
|
|
|
75
99
|
`actionIfNotSelfUser('read')` returns the given action only if the current user is **not** the target user. If the current user is the target (self), no action is returned and the ACL check is skipped entirely.
|
|
76
100
|
|
|
@@ -128,11 +152,11 @@ It is sometimes necessary to check ACLs based on the body of the request sent by
|
|
|
128
152
|
*
|
|
129
153
|
* Required privileges:
|
|
130
154
|
* - resource: vm, action: update (grants all fields)
|
|
131
|
-
* - resource: vm, action: update:
|
|
132
|
-
* - resource: vm, action: update:
|
|
155
|
+
* - resource: vm, action: update:nameLabel (if nameLabel is passed)
|
|
156
|
+
* - resource: vm, action: update:nameDescription (if nameDescription is passed)
|
|
133
157
|
*/
|
|
134
158
|
@Patch('{id}')
|
|
135
|
-
@Middlewares(acl({resource: 'vm', actions: actionsFromBody(['update:
|
|
159
|
+
@Middlewares(acl({resource: 'vm', actions: actionsFromBody(['update:nameLabel', 'update:nameDescription']), objectId: 'params.id'}))
|
|
136
160
|
@Response(403)
|
|
137
161
|
createVdi(@Path() id: string, @Body() body: patchBody) {
|
|
138
162
|
updateVm(id, body)
|
|
@@ -162,6 +186,14 @@ getUser(@Path() id: string) { ... }
|
|
|
162
186
|
|
|
163
187
|
If you need to use a privilege that doesn't exist yet (e.g., `resource: 'vm', action: 'foo'`), you must register it in ACL Definition: here `@xen-orchestra/acl/src/actions/vm.mts`, add: `foo: true`.
|
|
164
188
|
|
|
189
|
+
### MCP exposure
|
|
190
|
+
|
|
191
|
+
All REST API endpoints must define an MCP exposure policy using the `@Extension` decorator.
|
|
192
|
+
|
|
193
|
+
- `@Extension('x-mcp-exposure', 'allow')` for all `GET` endpoints.
|
|
194
|
+
- `@Extension('x-mcp-exposure', 'confirm')` for all non-`GET` endpoints (`POST`, `PATCH`, `PUT`, `DELETE`, etc.).
|
|
195
|
+
- `@Extension('x-mcp-exposure', 'deny')` only for exceptional cases.
|
|
196
|
+
|
|
165
197
|
## Contributions
|
|
166
198
|
|
|
167
199
|
Contributions are _very_ welcomed, either on the documentation or on
|
|
@@ -45,6 +45,10 @@ export class BackupJobService {
|
|
|
45
45
|
if (schedule.enabled) {
|
|
46
46
|
return true;
|
|
47
47
|
}
|
|
48
|
+
const scheduleSequence = await this.#restApi.xoApp.findEnabledScheduleSequenceFromSchedule(schedule.id);
|
|
49
|
+
if (scheduleSequence !== undefined) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
48
52
|
}
|
|
49
53
|
catch (error) {
|
|
50
54
|
if (!noSuchObject.is(error, { id: maybeScheduleId, type: 'schedule' })) {
|
|
@@ -7,17 +7,23 @@ 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, Extension, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
|
|
10
|
+
import { Body, Example, Extension, Get, Middlewares, Patch, 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 {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
13
|
+
import { json } from 'express';
|
|
14
|
+
import { forbiddenOperation } from 'xo-common/api-errors.js';
|
|
15
|
+
import { acl, actionsFromBody } from '../middlewares/acl.middleware.mjs';
|
|
16
|
+
import { badRequestResp, createdResp, forbiddenOperationResp, invalidParameters, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
17
|
+
import { backupRepositoryIds, partialBackupRepositories, backupRepository, backupRepositoryId, } from '../open-api/oa-examples/backup-repository.oa-example.mjs';
|
|
16
18
|
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
17
19
|
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
20
|
+
import { BackupRepositoryService } from './backup-repository.service.mjs';
|
|
21
|
+
import { taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
|
|
18
22
|
let BackupRepositoryController = class BackupRepositoryController extends XoController {
|
|
19
|
-
|
|
23
|
+
#backupRepositoryService;
|
|
24
|
+
constructor(restApi, backupRepositoryService) {
|
|
20
25
|
super('backup-repository', restApi);
|
|
26
|
+
this.#backupRepositoryService = backupRepositoryService;
|
|
21
27
|
}
|
|
22
28
|
// --- abstract methods
|
|
23
29
|
getAllCollectionObjects() {
|
|
@@ -40,6 +46,16 @@ let BackupRepositoryController = class BackupRepositoryController extends XoCont
|
|
|
40
46
|
privilege: { action: 'read', resource: 'backup-repository' },
|
|
41
47
|
});
|
|
42
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Required privilege:
|
|
51
|
+
* - resource: backup-repository, action: create
|
|
52
|
+
*
|
|
53
|
+
* @example body { "name": "NFS Remote", "options": "vers=4", "proxy": "722d17b9-699b-59d2-8193-be1ac573d3de", "url": "nfs://192.168.100.225:/media/nfs" }
|
|
54
|
+
*/
|
|
55
|
+
async createBackupRepository(body) {
|
|
56
|
+
const backupRepository = await this.restApi.xoApp.createRemote(body);
|
|
57
|
+
return { id: backupRepository.id };
|
|
58
|
+
}
|
|
43
59
|
/**
|
|
44
60
|
* Required privilege:
|
|
45
61
|
* - resource: backup-repository, action: read
|
|
@@ -49,6 +65,48 @@ let BackupRepositoryController = class BackupRepositoryController extends XoCont
|
|
|
49
65
|
getRepository(id) {
|
|
50
66
|
return this.getObject(id);
|
|
51
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Forgets a backup repository configuration.
|
|
70
|
+
*
|
|
71
|
+
* A backup repository cannot be forgotten if it is referenced by any backup job (enabled or disabled).
|
|
72
|
+
*
|
|
73
|
+
* Required privilege:
|
|
74
|
+
* - resource: backup-repository, action: forget
|
|
75
|
+
*
|
|
76
|
+
* @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
|
|
77
|
+
*/
|
|
78
|
+
async forgetBackupRepository(id, sync) {
|
|
79
|
+
const repositoryId = id;
|
|
80
|
+
const action = async () => {
|
|
81
|
+
const referencingJobs = await this.#backupRepositoryService.getReferencingJobs(repositoryId);
|
|
82
|
+
if (referencingJobs.length > 0) {
|
|
83
|
+
throw forbiddenOperation('forget backup repository', `repository is referenced by ${referencingJobs.length} backup job(s): ${referencingJobs.join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
await this.restApi.xoApp.removeRemote(repositoryId);
|
|
86
|
+
};
|
|
87
|
+
return this.createAction(action, {
|
|
88
|
+
sync,
|
|
89
|
+
statusCode: noContentResp.status,
|
|
90
|
+
taskProperties: {
|
|
91
|
+
name: 'forget backup repository',
|
|
92
|
+
objectId: repositoryId,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/** Required privileges:
|
|
97
|
+
* - resource: backup-repository, action: update (grants all fields)
|
|
98
|
+
* - resource: backup-repository, action: update:enabled (if enabled is passed)
|
|
99
|
+
* - resource: backup-repository, action: update:name (if name is passed)
|
|
100
|
+
* - resource: backup-repository, action: update:options (if options is passed)
|
|
101
|
+
* - resource: backup-repository, action: update:proxy (if proxy is passed)
|
|
102
|
+
* - resource: backup-repository, action: update:url (if url is passed)
|
|
103
|
+
*
|
|
104
|
+
* @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
|
|
105
|
+
* @example body { "enabled": true, "name": "NFS Remote", "options": "vers=4", "proxy": "722d17b9-699b-59d2-8193-be1ac573d3de", "url": "nfs://192.168.100.225:/media/nfs" }
|
|
106
|
+
*/
|
|
107
|
+
async updateBackupRepository(id, body) {
|
|
108
|
+
await this.restApi.xoApp.updateRemote(id, body);
|
|
109
|
+
}
|
|
52
110
|
};
|
|
53
111
|
__decorate([
|
|
54
112
|
Example(backupRepositoryIds),
|
|
@@ -63,6 +121,22 @@ __decorate([
|
|
|
63
121
|
__param(4, Query()),
|
|
64
122
|
__param(5, Query())
|
|
65
123
|
], BackupRepositoryController.prototype, "getRepositories", null);
|
|
124
|
+
__decorate([
|
|
125
|
+
Example(backupRepositoryId),
|
|
126
|
+
Post(''),
|
|
127
|
+
Middlewares([
|
|
128
|
+
json(),
|
|
129
|
+
acl({
|
|
130
|
+
resource: 'backup-repository',
|
|
131
|
+
action: 'create',
|
|
132
|
+
object: ({ req }) => req.body,
|
|
133
|
+
}),
|
|
134
|
+
]),
|
|
135
|
+
SuccessResponse(createdResp.status, createdResp.description),
|
|
136
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
137
|
+
Response(invalidParameters.status, invalidParameters.description),
|
|
138
|
+
__param(0, Body())
|
|
139
|
+
], BackupRepositoryController.prototype, "createBackupRepository", null);
|
|
66
140
|
__decorate([
|
|
67
141
|
Example(backupRepository),
|
|
68
142
|
Extension('x-mcp-exposure', 'allow'),
|
|
@@ -77,6 +151,40 @@ __decorate([
|
|
|
77
151
|
Response(notFoundResp.status, notFoundResp.description),
|
|
78
152
|
__param(0, Path())
|
|
79
153
|
], BackupRepositoryController.prototype, "getRepository", null);
|
|
154
|
+
__decorate([
|
|
155
|
+
Example(taskLocation),
|
|
156
|
+
Extension('x-mcp-exposure', 'confirm'),
|
|
157
|
+
Post('{id}/actions/forget'),
|
|
158
|
+
Middlewares(acl({
|
|
159
|
+
resource: 'backup-repository',
|
|
160
|
+
action: 'forget',
|
|
161
|
+
objectId: 'params.id',
|
|
162
|
+
getObject: ({ restApi }) => restApi.xoApp.getRemote,
|
|
163
|
+
})),
|
|
164
|
+
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
165
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
166
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
167
|
+
__param(0, Path()),
|
|
168
|
+
__param(1, Query())
|
|
169
|
+
], BackupRepositoryController.prototype, "forgetBackupRepository", null);
|
|
170
|
+
__decorate([
|
|
171
|
+
Patch('{id}'),
|
|
172
|
+
Middlewares([
|
|
173
|
+
json(),
|
|
174
|
+
acl({
|
|
175
|
+
resource: 'backup-repository',
|
|
176
|
+
actions: actionsFromBody(['update:enabled', 'update:name', 'update:options', 'update:proxy', 'update:url']),
|
|
177
|
+
objectId: 'params.id',
|
|
178
|
+
getObject: ({ restApi }) => restApi.xoApp.getRemote,
|
|
179
|
+
}),
|
|
180
|
+
]),
|
|
181
|
+
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
182
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
183
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
184
|
+
Response(invalidParameters.status, invalidParameters.description),
|
|
185
|
+
__param(0, Path()),
|
|
186
|
+
__param(1, Body())
|
|
187
|
+
], BackupRepositoryController.prototype, "updateBackupRepository", null);
|
|
80
188
|
BackupRepositoryController = __decorate([
|
|
81
189
|
Route('backup-repositories'),
|
|
82
190
|
Security('*'),
|
|
@@ -84,6 +192,7 @@ BackupRepositoryController = __decorate([
|
|
|
84
192
|
Response(unauthorizedResp.status, unauthorizedResp.description),
|
|
85
193
|
Tags('backup-repositories'),
|
|
86
194
|
provide(BackupRepositoryController),
|
|
87
|
-
__param(0, inject(RestApi))
|
|
195
|
+
__param(0, inject(RestApi)),
|
|
196
|
+
__param(1, inject(BackupRepositoryService))
|
|
88
197
|
], BackupRepositoryController);
|
|
89
198
|
export { BackupRepositoryController };
|
|
@@ -0,0 +1,46 @@
|
|
|
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 { RestApi } from '../rest-api/rest-api.mjs';
|
|
11
|
+
import { inject } from 'inversify';
|
|
12
|
+
let BackupRepositoryService = class BackupRepositoryService {
|
|
13
|
+
#restApi;
|
|
14
|
+
constructor(restApi) {
|
|
15
|
+
this.#restApi = restApi;
|
|
16
|
+
}
|
|
17
|
+
isBackupRepositoryReferenced(idsToCheck, repositoryId) {
|
|
18
|
+
if (idsToCheck === undefined) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const { id } = idsToCheck;
|
|
22
|
+
const ids = typeof id === 'string' ? [id] : id.__or;
|
|
23
|
+
return ids.includes(repositoryId);
|
|
24
|
+
}
|
|
25
|
+
async getReferencingJobs(repositoryId) {
|
|
26
|
+
const allJobs = await this.#restApi.xoApp.getAllJobs();
|
|
27
|
+
const referencingJobs = [];
|
|
28
|
+
for (const job of allJobs) {
|
|
29
|
+
if (job.type === 'backup' || job.type === 'metadataBackup') {
|
|
30
|
+
if (this.isBackupRepositoryReferenced(job.remotes, repositoryId)) {
|
|
31
|
+
referencingJobs.push(job.id);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else if (job.type === 'mirrorBackup') {
|
|
35
|
+
if (job.sourceRemote === repositoryId || this.isBackupRepositoryReferenced(job.remotes, repositoryId)) {
|
|
36
|
+
referencingJobs.push(job.id);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return referencingJobs;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
BackupRepositoryService = __decorate([
|
|
44
|
+
__param(0, inject(RestApi))
|
|
45
|
+
], BackupRepositoryService);
|
|
46
|
+
export { BackupRepositoryService };
|
package/dist/ioc/ioc.mjs
CHANGED
|
@@ -16,6 +16,7 @@ import { NetworkService } from '../networks/network.service.mjs';
|
|
|
16
16
|
import { BackupArchiveService } from '../backup-archives/backup-archive.service.mjs';
|
|
17
17
|
import { SrService } from '../srs/sr.service.mjs';
|
|
18
18
|
import { LicenseService } from '../licenses/license.service.mjs';
|
|
19
|
+
import { BackupRepositoryService } from '../backup-repositories/backup-repository.service.mjs';
|
|
19
20
|
const iocContainer = new Container();
|
|
20
21
|
export function setupContainer(xoApp) {
|
|
21
22
|
decorate(injectable(), Controller);
|
|
@@ -125,5 +126,12 @@ export function setupContainer(xoApp) {
|
|
|
125
126
|
return new LicenseService(restApi);
|
|
126
127
|
})
|
|
127
128
|
.inSingletonScope();
|
|
129
|
+
iocContainer
|
|
130
|
+
.bind(BackupRepositoryService)
|
|
131
|
+
.toDynamicValue(ctx => {
|
|
132
|
+
const restApi = ctx.container.get(RestApi);
|
|
133
|
+
return new BackupRepositoryService(restApi);
|
|
134
|
+
})
|
|
135
|
+
.inSingletonScope();
|
|
128
136
|
}
|
|
129
137
|
export { iocContainer };
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { getMissingPrivileges, } from '@xen-orchestra/acl';
|
|
2
|
+
import { createLogger } from '@xen-orchestra/log';
|
|
2
3
|
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
3
4
|
import { iocContainer } from '../ioc/ioc.mjs';
|
|
4
5
|
import { ValidateError } from 'tsoa';
|
|
5
6
|
import { ApiError } from '../helpers/error.helper.mjs';
|
|
6
7
|
export const ACL_MIDDLEWARE_NAME = '_aclMiddleware';
|
|
8
|
+
const log = createLogger('xo:rest-api:middleware');
|
|
7
9
|
export function actionsFromBody(actions) {
|
|
8
10
|
return ({ req }) => actions.filter(action => {
|
|
9
11
|
const [, field] = action.split(':');
|
|
@@ -107,7 +109,14 @@ export function acl(acls) {
|
|
|
107
109
|
const _acls = acls.map(normalizeAclEntry);
|
|
108
110
|
async function middleware(req, res, next) {
|
|
109
111
|
const restApi = iocContainer.get(RestApi);
|
|
110
|
-
|
|
112
|
+
let user;
|
|
113
|
+
try {
|
|
114
|
+
user = restApi.getCurrentUser();
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
log.warn('An unauthenticated user made its way to acl middleware', error);
|
|
118
|
+
return next(error);
|
|
119
|
+
}
|
|
111
120
|
const invalidFields = {};
|
|
112
121
|
const missingPrivilegeParams = [];
|
|
113
122
|
for (const acl of _acls) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createLogger } from '@xen-orchestra/log';
|
|
2
|
+
const log = createLogger('xo:rest-api:deprecated.middleware');
|
|
3
|
+
export function vmExportCompressDeprecated(req, _res, next) {
|
|
4
|
+
const compress = req.query.compress;
|
|
5
|
+
if (compress === 'true' || compress === 'false') {
|
|
6
|
+
log.warn("the query param 'compress' as boolean is deprecated. Please use an explicit value next time");
|
|
7
|
+
req.query.compress = compress === 'true' ? 'gzip' : undefined;
|
|
8
|
+
}
|
|
9
|
+
next();
|
|
10
|
+
}
|
|
@@ -29,6 +29,7 @@ export default function genericErrorHandler(error, req, res, _next) {
|
|
|
29
29
|
}
|
|
30
30
|
else if (invalidCredentials.is(error)) {
|
|
31
31
|
statusCode = 401;
|
|
32
|
+
res.setHeader('WWW-Authenticate', 'Basic realm="xo"');
|
|
32
33
|
}
|
|
33
34
|
else if (objectAlreadyExists.is(error)) {
|
|
34
35
|
statusCode = 409;
|
|
@@ -29,3 +29,4 @@ export const backupRepository = {
|
|
|
29
29
|
id: '677e50c5-8d8a-4c89-b1ac-e2f4593d0ebb',
|
|
30
30
|
url: 's3://FOIS5DY532RGXD62TJ52:obfuscated-q3oi6d9X8uenGvdLnHk2@s3.us-east-2.amazonaws.com/with-lock/backup?useVhdDirectory=true',
|
|
31
31
|
};
|
|
32
|
+
export const backupRepositoryId = { id: '677e50c5-8d8a-4c89-b1ac-e2f4593d0ebb' };
|