mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-11-09 17:06:13 -06:00
Compare commits
8 Commits
326c152fac
...
pbt_backup
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab8b37587c | ||
|
|
ab194deedf | ||
|
|
89c42a5f96 | ||
|
|
81ecec19c8 | ||
|
|
e180bdc3cf | ||
|
|
267f0f0306 | ||
|
|
d6e6babb26 | ||
|
|
821dfdb710 |
22
@xen-orchestra/rest-api/src/backups/jobs.service.mts
Normal file
22
@xen-orchestra/rest-api/src/backups/jobs.service.mts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { BACKUP_TYPE, XoBackupJob, XoMetadataBackupJob, XoMirrorBackupJob } from '@vates/types'
|
||||
import { RestApi } from '../rest-api/rest-api.mjs'
|
||||
|
||||
export class JobService {
|
||||
#restApi: RestApi
|
||||
|
||||
constructor(restApi: RestApi) {
|
||||
this.#restApi = restApi
|
||||
}
|
||||
|
||||
async getVmJobs(): Promise<XoBackupJob[]> {
|
||||
return this.#restApi.xoApp.getAllJobs(BACKUP_TYPE.backup)
|
||||
}
|
||||
|
||||
async getMetadataJobs(): Promise<XoMetadataBackupJob[]> {
|
||||
return this.#restApi.xoApp.getAllJobs(BACKUP_TYPE.metadata)
|
||||
}
|
||||
|
||||
async getMirrorJobs(): Promise<XoMirrorBackupJob[]> {
|
||||
return this.#restApi.xoApp.getAllJobs(BACKUP_TYPE.mirror)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa'
|
||||
import { provide } from 'inversify-binding-decorators'
|
||||
import { Request as ExRequest } from 'express'
|
||||
import type { Branded, XoJob, XoMetadataBackupJob } from '@vates/types'
|
||||
|
||||
import { notFoundResp, type Unbrand } from '../../open-api/common/response.common.mjs'
|
||||
import type { SendObjects } from '../../helpers/helper.type.mjs'
|
||||
import { XoController } from '../../abstract-classes/xo-controller.mjs'
|
||||
import { RestApi } from '../../rest-api/rest-api.mjs'
|
||||
import { inject } from 'inversify'
|
||||
import { JobService } from '../jobs.service.mjs'
|
||||
import { job, jobIds, partialJobs } from '../../open-api/oa-examples/jobs.oa-example.mjs'
|
||||
|
||||
type UnbrandedXoMetadataJob = Unbrand<Omit<XoMetadataBackupJob, 'settings'>>
|
||||
|
||||
@Security('*')
|
||||
@Route('backup/jobs')
|
||||
@Response(notFoundResp.status, notFoundResp.description)
|
||||
@Tags('backup')
|
||||
@provide(MetadataJobController)
|
||||
export class MetadataJobController extends XoController<XoMetadataBackupJob> {
|
||||
#jobService: JobService
|
||||
constructor(@inject(RestApi) restApi: RestApi) {
|
||||
super(restApi)
|
||||
this.#jobService = new JobService(this.restApi)
|
||||
}
|
||||
async getAllCollectionObjects(): Promise<XoMetadataBackupJob[]> {
|
||||
return this.#jobService.getMetadataJobs()
|
||||
}
|
||||
|
||||
async getCollectionObject(id: Branded<'job'>): Promise<XoMetadataBackupJob> {
|
||||
return this.restApi.xoApp.getJob(id) as Promise<XoMetadataBackupJob>
|
||||
}
|
||||
|
||||
/**
|
||||
* @example fields "name,type,remotes"
|
||||
* @example filter "name:test"
|
||||
* @example limit 42
|
||||
*/
|
||||
@Example(jobIds)
|
||||
@Example(partialJobs)
|
||||
@Get('metadata')
|
||||
async getMetadataJobs(
|
||||
@Request() req: ExRequest,
|
||||
@Query() fields?: string,
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
): Promise<SendObjects<UnbrandedXoMetadataJob>> {
|
||||
return this.sendObjects(Object.values(await this.getObjects({ filter, limit })), req)
|
||||
}
|
||||
|
||||
/**
|
||||
* @example id "7d98fee4-3357-41a7-ac3f-9124212badb7"
|
||||
*/
|
||||
@Get('metadata/{id}')
|
||||
@Response(notFoundResp.status, notFoundResp.description)
|
||||
async getMetadataJob(@Path() id: string): Promise<UnbrandedXoMetadataJob> {
|
||||
return this.getObject(id as XoJob['id'])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa'
|
||||
import { provide } from 'inversify-binding-decorators'
|
||||
import { Request as ExRequest } from 'express'
|
||||
import type { Branded, XoJob, XoMirrorBackupJob } from '@vates/types'
|
||||
|
||||
import { notFoundResp, type Unbrand } from '../../open-api/common/response.common.mjs'
|
||||
import type { SendObjects } from '../../helpers/helper.type.mjs'
|
||||
import { XoController } from '../../abstract-classes/xo-controller.mjs'
|
||||
import { RestApi } from '../../rest-api/rest-api.mjs'
|
||||
import { inject } from 'inversify'
|
||||
import { JobService } from '../jobs.service.mjs'
|
||||
import { job, jobIds, partialJobs } from '../../open-api/oa-examples/jobs.oa-example.mjs'
|
||||
|
||||
type UnbrandedXoMirrorJob = Unbrand<Omit<XoMirrorBackupJob, 'settings'>>
|
||||
|
||||
@Security('*')
|
||||
@Route('backup/jobs')
|
||||
@Response(notFoundResp.status, notFoundResp.description)
|
||||
@Tags('backup')
|
||||
@provide(MirrorJobController)
|
||||
export class MirrorJobController extends XoController<XoMirrorBackupJob> {
|
||||
#jobService: JobService
|
||||
constructor(@inject(RestApi) restApi: RestApi) {
|
||||
super(restApi)
|
||||
this.#jobService = new JobService(this.restApi)
|
||||
}
|
||||
async getAllCollectionObjects(): Promise<XoMirrorBackupJob[]> {
|
||||
return this.#jobService.getMirrorJobs()
|
||||
}
|
||||
|
||||
async getCollectionObject(id: Branded<'job'>): Promise<XoMirrorBackupJob> {
|
||||
return this.restApi.xoApp.getJob(id) as Promise<XoMirrorBackupJob>
|
||||
}
|
||||
|
||||
/**
|
||||
* @example fields "name,type,remotes"
|
||||
* @example filter "name:test"
|
||||
* @example limit 42
|
||||
*/
|
||||
@Example(jobIds)
|
||||
@Example(partialJobs)
|
||||
@Get('mirror')
|
||||
async getMirrorJobs(
|
||||
@Request() req: ExRequest,
|
||||
@Query() fields?: string,
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
): Promise<SendObjects<UnbrandedXoMirrorJob>> {
|
||||
return this.sendObjects(Object.values(await this.getObjects({ filter, limit })), req)
|
||||
}
|
||||
|
||||
/**
|
||||
* @example id "7d98fee4-3357-41a7-ac3f-9124212badb7"
|
||||
*/
|
||||
@Example(job)
|
||||
@Get('mirror/{id}')
|
||||
@Response(notFoundResp.status, notFoundResp.description)
|
||||
async getMirrorJob(@Path() id: string): Promise<UnbrandedXoMirrorJob> {
|
||||
return this.getObject(id as XoJob['id'])
|
||||
}
|
||||
}
|
||||
73
@xen-orchestra/rest-api/src/backups/vm/jobs.controller.mts
Normal file
73
@xen-orchestra/rest-api/src/backups/vm/jobs.controller.mts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { Example, Get, Path, Query, Request, Response, Route, Security, SuccessResponse, Tags } from 'tsoa'
|
||||
import { provide } from 'inversify-binding-decorators'
|
||||
import { Request as ExRequest, Response as ExResponse } from 'express'
|
||||
import type { Branded, XoBackupJob, XoJob } from '@vates/types'
|
||||
|
||||
import { notFoundResp, type Unbrand } from '../../open-api/common/response.common.mjs'
|
||||
import type { SendObjects } from '../../helpers/helper.type.mjs'
|
||||
import { XoController } from '../../abstract-classes/xo-controller.mjs'
|
||||
import { RestApi } from '../../rest-api/rest-api.mjs'
|
||||
import { inject } from 'inversify'
|
||||
import { JobService } from '../jobs.service.mjs'
|
||||
import { job, jobIds, partialJobs } from '../../open-api/oa-examples/jobs.oa-example.mjs'
|
||||
|
||||
type UnbrandedXoBackupJob = Unbrand<Omit<XoBackupJob, 'settings'>>
|
||||
|
||||
@Security('*')
|
||||
@Route('backup/jobs')
|
||||
@Response(notFoundResp.status, notFoundResp.description)
|
||||
@Tags('backup')
|
||||
@provide(VmJobController)
|
||||
export class VmJobController extends XoController<XoBackupJob> {
|
||||
#jobService: JobService
|
||||
constructor(@inject(RestApi) restApi: RestApi) {
|
||||
super(restApi)
|
||||
this.#jobService = new JobService(this.restApi)
|
||||
}
|
||||
async getAllCollectionObjects(): Promise<XoBackupJob[]> {
|
||||
return this.#jobService.getVmJobs()
|
||||
}
|
||||
|
||||
async getCollectionObject(id: Branded<'job'>): Promise<XoBackupJob> {
|
||||
return this.restApi.xoApp.getJob(id) as Promise<XoBackupJob>
|
||||
}
|
||||
|
||||
/**
|
||||
* @example fields "name,type,remotes"
|
||||
* @example filter "name:test"
|
||||
* @example limit 42
|
||||
*/
|
||||
@Example(jobIds)
|
||||
@Example(partialJobs)
|
||||
@Get('vm')
|
||||
async getVmJobs(
|
||||
@Request() req: ExRequest,
|
||||
@Query() fields?: string,
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
): Promise<SendObjects<UnbrandedXoBackupJob>> {
|
||||
return this.sendObjects(Object.values(await this.getObjects({ filter, limit })), req)
|
||||
}
|
||||
|
||||
/**
|
||||
* @example id "7d98fee4-3357-41a7-ac3f-9124212badb7"
|
||||
*/
|
||||
@Example(job)
|
||||
@Get('vm/{id}')
|
||||
@Response(notFoundResp.status, notFoundResp.description)
|
||||
async getVmJob(@Path() id: string): Promise<UnbrandedXoBackupJob> {
|
||||
return this.getObject(id as XoJob['id'])
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects from /backup/jobs/{id} to /backup/jobs/vm/{id}
|
||||
* @example id "7d98fee4-3357-41a7-ac3f-9124212badb7"
|
||||
*/
|
||||
@Get('{id}')
|
||||
@Response(308, 'Permanent Redirect')
|
||||
async redirectToVmJob(@Path() id: string, @Request() req: ExRequest): Promise<void> {
|
||||
const res = req.res as ExResponse
|
||||
res.redirect(308, `/rest/v0/backup/jobs/vm/${id}`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
export const jobIds = [
|
||||
'/rest/v0/backup/jobs/vm/d2511443-5fef-4750-b59e-7eca01d1eaab',
|
||||
'/rest/v0/backup/jobs/vm/73a76f43-c5cf-4644-878d-1f6c0e41c8b8',
|
||||
]
|
||||
|
||||
export const partialJobs = [
|
||||
{
|
||||
name: 'Azurite delta',
|
||||
type: 'backup',
|
||||
remotes: {
|
||||
id: 'a72c53c1-eae1-43f6-be6d-7b0342a1f218',
|
||||
},
|
||||
href: '/rest/v0/backup/jobs/vm/d2511443-5fef-4750-b59e-7eca01d1eaab',
|
||||
},
|
||||
{
|
||||
name: 'Backup QA Auto',
|
||||
type: 'backup',
|
||||
remotes: {
|
||||
id: 'a72c53c1-eae1-43f6-be6d-7b0342a1f218',
|
||||
},
|
||||
href: '/rest/v0/backup/jobs/vm/73a76f43-c5cf-4644-878d-1f6c0e41c8b8',
|
||||
},
|
||||
]
|
||||
|
||||
export const job = {
|
||||
name: 'Azurite delta',
|
||||
mode: 'delta',
|
||||
settings: {
|
||||
'': {
|
||||
timezone: 'Europe/Paris',
|
||||
preferNbd: true,
|
||||
},
|
||||
'ee3d0a7c-6f4c-4664-822cdc4953f2a571': {
|
||||
exportRetention: 1,
|
||||
},
|
||||
},
|
||||
remotes: {
|
||||
id: 'a72c53c1-eae1-43f6-be6d-7b0342a1f218',
|
||||
},
|
||||
vms: {
|
||||
id: '7b894929-273e-15f4-b7df-88a497d90c14',
|
||||
},
|
||||
type: 'backup',
|
||||
srs: {
|
||||
id: {
|
||||
__or: [],
|
||||
},
|
||||
},
|
||||
id: 'd2511443-5fef-4750-b59e-7eca01d1eaab',
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import type {
|
||||
XenApiVtpmWrapped,
|
||||
} from '@vates/types/xen-api'
|
||||
import type {
|
||||
AnyXoJob,
|
||||
XoBackupRepository,
|
||||
XoHost,
|
||||
XoServer,
|
||||
@@ -36,6 +35,7 @@ import type {
|
||||
XoTask,
|
||||
XoProxy,
|
||||
AnyXoBackupJob,
|
||||
AnyXoJob,
|
||||
} from '@vates/types/xo'
|
||||
|
||||
import type { InsertableXoServer } from '../servers/server.type.mjs'
|
||||
|
||||
@@ -784,14 +784,6 @@ export default class RestApi {
|
||||
res.redirect(307, req.baseUrl + '/users/' + user.id + (req.params[0] ?? ''))
|
||||
})
|
||||
|
||||
const backupTypes = {
|
||||
__proto__: null,
|
||||
|
||||
metadata: 'metadataBackup',
|
||||
mirror: 'mirrorBackup',
|
||||
vm: 'backup',
|
||||
}
|
||||
|
||||
api
|
||||
.get(
|
||||
'/backup',
|
||||
@@ -808,35 +800,6 @@ export default class RestApi {
|
||||
await sendObjects(logs, req, res)
|
||||
})
|
||||
)
|
||||
.get(
|
||||
'/backup/jobs',
|
||||
wrap((req, res) =>
|
||||
sendObjects(
|
||||
Object.keys(backupTypes).map(id => ({ id })),
|
||||
req,
|
||||
res
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
for (const [collection, type] of Object.entries(backupTypes)) {
|
||||
api
|
||||
.get(
|
||||
'/backup/jobs/' + collection,
|
||||
wrap(async (req, res) => sendObjects(await app.getAllJobs(type), req, res))
|
||||
)
|
||||
.get(
|
||||
`/backup/jobs/${collection}/:id`,
|
||||
wrap(async (req, res) => {
|
||||
res.json(await app.getJob(req.params.id, type))
|
||||
}, true)
|
||||
)
|
||||
}
|
||||
|
||||
// For compatibility, redirect /backup/jobs/:id to /backup/jobs/vm/:id
|
||||
api.get('/backup/jobs/:id', (req, res) => {
|
||||
res.redirect(308, req.baseUrl + '/backup/jobs/vm/' + req.params.id)
|
||||
})
|
||||
|
||||
api
|
||||
.get(
|
||||
@@ -861,15 +824,14 @@ export default class RestApi {
|
||||
}, true)
|
||||
)
|
||||
|
||||
api
|
||||
.get(
|
||||
'/tasks/:id/actions',
|
||||
wrap(async (req, res) => {
|
||||
const task = await app.tasks.get(req.params.id)
|
||||
api.get(
|
||||
'/tasks/:id/actions',
|
||||
wrap(async (req, res) => {
|
||||
const task = await app.tasks.get(req.params.id)
|
||||
|
||||
await sendObjects(task.status === 'pending' ? [{ id: 'abort' }] : [], req, res)
|
||||
})
|
||||
)
|
||||
await sendObjects(task.status === 'pending' ? [{ id: 'abort' }] : [], req, res)
|
||||
})
|
||||
)
|
||||
|
||||
api.get(
|
||||
'/:collection',
|
||||
|
||||
Reference in New Issue
Block a user