refactor: always use short id in api calls
This commit is contained in:
parent
f3608b0f30
commit
7cb274f76d
51 changed files with 562 additions and 495 deletions
|
|
@ -1288,7 +1288,7 @@ export const getMirrorCompatibilityOptions = (options: Options<GetMirrorCompatib
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reorder backup schedules by providing an array of schedule IDs in the desired order
|
* Reorder backup schedules by providing an array of schedule short IDs in the desired order
|
||||||
*/
|
*/
|
||||||
export const reorderBackupSchedulesMutation = (
|
export const reorderBackupSchedulesMutation = (
|
||||||
options?: Partial<Options<ReorderBackupSchedulesData>>,
|
options?: Partial<Options<ReorderBackupSchedulesData>>,
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ export const testConnection = <ThrowOnError extends boolean = false>(
|
||||||
*/
|
*/
|
||||||
export const deleteVolume = <ThrowOnError extends boolean = false>(options: Options<DeleteVolumeData, ThrowOnError>) =>
|
export const deleteVolume = <ThrowOnError extends boolean = false>(options: Options<DeleteVolumeData, ThrowOnError>) =>
|
||||||
(options.client ?? client).delete<DeleteVolumeResponses, unknown, ThrowOnError>({
|
(options.client ?? client).delete<DeleteVolumeResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/volumes/{id}",
|
url: "/api/v1/volumes/{shortId}",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -219,7 +219,7 @@ export const deleteVolume = <ThrowOnError extends boolean = false>(options: Opti
|
||||||
*/
|
*/
|
||||||
export const getVolume = <ThrowOnError extends boolean = false>(options: Options<GetVolumeData, ThrowOnError>) =>
|
export const getVolume = <ThrowOnError extends boolean = false>(options: Options<GetVolumeData, ThrowOnError>) =>
|
||||||
(options.client ?? client).get<GetVolumeResponses, GetVolumeErrors, ThrowOnError>({
|
(options.client ?? client).get<GetVolumeResponses, GetVolumeErrors, ThrowOnError>({
|
||||||
url: "/api/v1/volumes/{id}",
|
url: "/api/v1/volumes/{shortId}",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -228,7 +228,7 @@ export const getVolume = <ThrowOnError extends boolean = false>(options: Options
|
||||||
*/
|
*/
|
||||||
export const updateVolume = <ThrowOnError extends boolean = false>(options: Options<UpdateVolumeData, ThrowOnError>) =>
|
export const updateVolume = <ThrowOnError extends boolean = false>(options: Options<UpdateVolumeData, ThrowOnError>) =>
|
||||||
(options.client ?? client).put<UpdateVolumeResponses, UpdateVolumeErrors, ThrowOnError>({
|
(options.client ?? client).put<UpdateVolumeResponses, UpdateVolumeErrors, ThrowOnError>({
|
||||||
url: "/api/v1/volumes/{id}",
|
url: "/api/v1/volumes/{shortId}",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -241,7 +241,7 @@ export const updateVolume = <ThrowOnError extends boolean = false>(options: Opti
|
||||||
*/
|
*/
|
||||||
export const mountVolume = <ThrowOnError extends boolean = false>(options: Options<MountVolumeData, ThrowOnError>) =>
|
export const mountVolume = <ThrowOnError extends boolean = false>(options: Options<MountVolumeData, ThrowOnError>) =>
|
||||||
(options.client ?? client).post<MountVolumeResponses, unknown, ThrowOnError>({
|
(options.client ?? client).post<MountVolumeResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/volumes/{id}/mount",
|
url: "/api/v1/volumes/{shortId}/mount",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -252,7 +252,7 @@ export const unmountVolume = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<UnmountVolumeData, ThrowOnError>,
|
options: Options<UnmountVolumeData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).post<UnmountVolumeResponses, unknown, ThrowOnError>({
|
(options.client ?? client).post<UnmountVolumeResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/volumes/{id}/unmount",
|
url: "/api/v1/volumes/{shortId}/unmount",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -263,7 +263,7 @@ export const healthCheckVolume = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<HealthCheckVolumeData, ThrowOnError>,
|
options: Options<HealthCheckVolumeData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).post<HealthCheckVolumeResponses, HealthCheckVolumeErrors, ThrowOnError>({
|
(options.client ?? client).post<HealthCheckVolumeResponses, HealthCheckVolumeErrors, ThrowOnError>({
|
||||||
url: "/api/v1/volumes/{id}/health-check",
|
url: "/api/v1/volumes/{shortId}/health-check",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -272,7 +272,7 @@ export const healthCheckVolume = <ThrowOnError extends boolean = false>(
|
||||||
*/
|
*/
|
||||||
export const listFiles = <ThrowOnError extends boolean = false>(options: Options<ListFilesData, ThrowOnError>) =>
|
export const listFiles = <ThrowOnError extends boolean = false>(options: Options<ListFilesData, ThrowOnError>) =>
|
||||||
(options.client ?? client).get<ListFilesResponses, unknown, ThrowOnError>({
|
(options.client ?? client).get<ListFilesResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/volumes/{id}/files",
|
url: "/api/v1/volumes/{shortId}/files",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -331,7 +331,7 @@ export const deleteRepository = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<DeleteRepositoryData, ThrowOnError>,
|
options: Options<DeleteRepositoryData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).delete<DeleteRepositoryResponses, unknown, ThrowOnError>({
|
(options.client ?? client).delete<DeleteRepositoryResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}",
|
url: "/api/v1/repositories/{shortId}",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -342,7 +342,7 @@ export const getRepository = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<GetRepositoryData, ThrowOnError>,
|
options: Options<GetRepositoryData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).get<GetRepositoryResponses, unknown, ThrowOnError>({
|
(options.client ?? client).get<GetRepositoryResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}",
|
url: "/api/v1/repositories/{shortId}",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -353,7 +353,7 @@ export const updateRepository = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<UpdateRepositoryData, ThrowOnError>,
|
options: Options<UpdateRepositoryData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).patch<UpdateRepositoryResponses, UpdateRepositoryErrors, ThrowOnError>({
|
(options.client ?? client).patch<UpdateRepositoryResponses, UpdateRepositoryErrors, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}",
|
url: "/api/v1/repositories/{shortId}",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -368,7 +368,7 @@ export const deleteSnapshots = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<DeleteSnapshotsData, ThrowOnError>,
|
options: Options<DeleteSnapshotsData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).delete<DeleteSnapshotsResponses, unknown, ThrowOnError>({
|
(options.client ?? client).delete<DeleteSnapshotsResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/snapshots",
|
url: "/api/v1/repositories/{shortId}/snapshots",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -383,7 +383,7 @@ export const listSnapshots = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<ListSnapshotsData, ThrowOnError>,
|
options: Options<ListSnapshotsData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).get<ListSnapshotsResponses, unknown, ThrowOnError>({
|
(options.client ?? client).get<ListSnapshotsResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/snapshots",
|
url: "/api/v1/repositories/{shortId}/snapshots",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -394,7 +394,7 @@ export const refreshSnapshots = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<RefreshSnapshotsData, ThrowOnError>,
|
options: Options<RefreshSnapshotsData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).post<RefreshSnapshotsResponses, unknown, ThrowOnError>({
|
(options.client ?? client).post<RefreshSnapshotsResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/snapshots/refresh",
|
url: "/api/v1/repositories/{shortId}/snapshots/refresh",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -405,7 +405,7 @@ export const deleteSnapshot = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<DeleteSnapshotData, ThrowOnError>,
|
options: Options<DeleteSnapshotData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).delete<DeleteSnapshotResponses, unknown, ThrowOnError>({
|
(options.client ?? client).delete<DeleteSnapshotResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/snapshots/{snapshotId}",
|
url: "/api/v1/repositories/{shortId}/snapshots/{snapshotId}",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -416,7 +416,7 @@ export const getSnapshotDetails = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<GetSnapshotDetailsData, ThrowOnError>,
|
options: Options<GetSnapshotDetailsData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).get<GetSnapshotDetailsResponses, unknown, ThrowOnError>({
|
(options.client ?? client).get<GetSnapshotDetailsResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/snapshots/{snapshotId}",
|
url: "/api/v1/repositories/{shortId}/snapshots/{snapshotId}",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -427,7 +427,7 @@ export const listSnapshotFiles = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<ListSnapshotFilesData, ThrowOnError>,
|
options: Options<ListSnapshotFilesData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).get<ListSnapshotFilesResponses, unknown, ThrowOnError>({
|
(options.client ?? client).get<ListSnapshotFilesResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/snapshots/{snapshotId}/files",
|
url: "/api/v1/repositories/{shortId}/snapshots/{snapshotId}/files",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -438,7 +438,7 @@ export const restoreSnapshot = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<RestoreSnapshotData, ThrowOnError>,
|
options: Options<RestoreSnapshotData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).post<RestoreSnapshotResponses, unknown, ThrowOnError>({
|
(options.client ?? client).post<RestoreSnapshotResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/restore",
|
url: "/api/v1/repositories/{shortId}/restore",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -451,7 +451,7 @@ export const restoreSnapshot = <ThrowOnError extends boolean = false>(
|
||||||
*/
|
*/
|
||||||
export const cancelDoctor = <ThrowOnError extends boolean = false>(options: Options<CancelDoctorData, ThrowOnError>) =>
|
export const cancelDoctor = <ThrowOnError extends boolean = false>(options: Options<CancelDoctorData, ThrowOnError>) =>
|
||||||
(options.client ?? client).delete<CancelDoctorResponses, CancelDoctorErrors, ThrowOnError>({
|
(options.client ?? client).delete<CancelDoctorResponses, CancelDoctorErrors, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/doctor",
|
url: "/api/v1/repositories/{shortId}/doctor",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -460,7 +460,7 @@ export const cancelDoctor = <ThrowOnError extends boolean = false>(options: Opti
|
||||||
*/
|
*/
|
||||||
export const startDoctor = <ThrowOnError extends boolean = false>(options: Options<StartDoctorData, ThrowOnError>) =>
|
export const startDoctor = <ThrowOnError extends boolean = false>(options: Options<StartDoctorData, ThrowOnError>) =>
|
||||||
(options.client ?? client).post<StartDoctorResponses, StartDoctorErrors, ThrowOnError>({
|
(options.client ?? client).post<StartDoctorResponses, StartDoctorErrors, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/doctor",
|
url: "/api/v1/repositories/{shortId}/doctor",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -471,7 +471,7 @@ export const unlockRepository = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<UnlockRepositoryData, ThrowOnError>,
|
options: Options<UnlockRepositoryData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).post<UnlockRepositoryResponses, unknown, ThrowOnError>({
|
(options.client ?? client).post<UnlockRepositoryResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/unlock",
|
url: "/api/v1/repositories/{shortId}/unlock",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -480,7 +480,7 @@ export const unlockRepository = <ThrowOnError extends boolean = false>(
|
||||||
*/
|
*/
|
||||||
export const tagSnapshots = <ThrowOnError extends boolean = false>(options: Options<TagSnapshotsData, ThrowOnError>) =>
|
export const tagSnapshots = <ThrowOnError extends boolean = false>(options: Options<TagSnapshotsData, ThrowOnError>) =>
|
||||||
(options.client ?? client).post<TagSnapshotsResponses, unknown, ThrowOnError>({
|
(options.client ?? client).post<TagSnapshotsResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/snapshots/tag",
|
url: "/api/v1/repositories/{shortId}/snapshots/tag",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -493,7 +493,7 @@ export const tagSnapshots = <ThrowOnError extends boolean = false>(options: Opti
|
||||||
*/
|
*/
|
||||||
export const devPanelExec = <ThrowOnError extends boolean = false>(options: Options<DevPanelExecData, ThrowOnError>) =>
|
export const devPanelExec = <ThrowOnError extends boolean = false>(options: Options<DevPanelExecData, ThrowOnError>) =>
|
||||||
(options.client ?? client).sse.post<DevPanelExecResponses, DevPanelExecErrors, ThrowOnError>({
|
(options.client ?? client).sse.post<DevPanelExecResponses, DevPanelExecErrors, ThrowOnError>({
|
||||||
url: "/api/v1/repositories/{id}/exec",
|
url: "/api/v1/repositories/{shortId}/exec",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -534,7 +534,7 @@ export const deleteBackupSchedule = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<DeleteBackupScheduleData, ThrowOnError>,
|
options: Options<DeleteBackupScheduleData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).delete<DeleteBackupScheduleResponses, unknown, ThrowOnError>({
|
(options.client ?? client).delete<DeleteBackupScheduleResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}",
|
url: "/api/v1/backups/{shortId}",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -545,7 +545,7 @@ export const getBackupSchedule = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<GetBackupScheduleData, ThrowOnError>,
|
options: Options<GetBackupScheduleData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).get<GetBackupScheduleResponses, unknown, ThrowOnError>({
|
(options.client ?? client).get<GetBackupScheduleResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}",
|
url: "/api/v1/backups/{shortId}",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -556,7 +556,7 @@ export const updateBackupSchedule = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<UpdateBackupScheduleData, ThrowOnError>,
|
options: Options<UpdateBackupScheduleData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).patch<UpdateBackupScheduleResponses, unknown, ThrowOnError>({
|
(options.client ?? client).patch<UpdateBackupScheduleResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}",
|
url: "/api/v1/backups/{shortId}",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -571,7 +571,7 @@ export const getBackupScheduleForVolume = <ThrowOnError extends boolean = false>
|
||||||
options: Options<GetBackupScheduleForVolumeData, ThrowOnError>,
|
options: Options<GetBackupScheduleForVolumeData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).get<GetBackupScheduleForVolumeResponses, unknown, ThrowOnError>({
|
(options.client ?? client).get<GetBackupScheduleForVolumeResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/volume/{volumeId}",
|
url: "/api/v1/backups/volume/{volumeShortId}",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -580,7 +580,7 @@ export const getBackupScheduleForVolume = <ThrowOnError extends boolean = false>
|
||||||
*/
|
*/
|
||||||
export const runBackupNow = <ThrowOnError extends boolean = false>(options: Options<RunBackupNowData, ThrowOnError>) =>
|
export const runBackupNow = <ThrowOnError extends boolean = false>(options: Options<RunBackupNowData, ThrowOnError>) =>
|
||||||
(options.client ?? client).post<RunBackupNowResponses, unknown, ThrowOnError>({
|
(options.client ?? client).post<RunBackupNowResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}/run",
|
url: "/api/v1/backups/{shortId}/run",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -589,7 +589,7 @@ export const runBackupNow = <ThrowOnError extends boolean = false>(options: Opti
|
||||||
*/
|
*/
|
||||||
export const stopBackup = <ThrowOnError extends boolean = false>(options: Options<StopBackupData, ThrowOnError>) =>
|
export const stopBackup = <ThrowOnError extends boolean = false>(options: Options<StopBackupData, ThrowOnError>) =>
|
||||||
(options.client ?? client).post<StopBackupResponses, StopBackupErrors, ThrowOnError>({
|
(options.client ?? client).post<StopBackupResponses, StopBackupErrors, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}/stop",
|
url: "/api/v1/backups/{shortId}/stop",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -598,7 +598,7 @@ export const stopBackup = <ThrowOnError extends boolean = false>(options: Option
|
||||||
*/
|
*/
|
||||||
export const runForget = <ThrowOnError extends boolean = false>(options: Options<RunForgetData, ThrowOnError>) =>
|
export const runForget = <ThrowOnError extends boolean = false>(options: Options<RunForgetData, ThrowOnError>) =>
|
||||||
(options.client ?? client).post<RunForgetResponses, unknown, ThrowOnError>({
|
(options.client ?? client).post<RunForgetResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}/forget",
|
url: "/api/v1/backups/{shortId}/forget",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -609,7 +609,7 @@ export const getScheduleNotifications = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<GetScheduleNotificationsData, ThrowOnError>,
|
options: Options<GetScheduleNotificationsData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).get<GetScheduleNotificationsResponses, unknown, ThrowOnError>({
|
(options.client ?? client).get<GetScheduleNotificationsResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}/notifications",
|
url: "/api/v1/backups/{shortId}/notifications",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -620,7 +620,7 @@ export const updateScheduleNotifications = <ThrowOnError extends boolean = false
|
||||||
options: Options<UpdateScheduleNotificationsData, ThrowOnError>,
|
options: Options<UpdateScheduleNotificationsData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).put<UpdateScheduleNotificationsResponses, unknown, ThrowOnError>({
|
(options.client ?? client).put<UpdateScheduleNotificationsResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}/notifications",
|
url: "/api/v1/backups/{shortId}/notifications",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -635,7 +635,7 @@ export const getScheduleMirrors = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<GetScheduleMirrorsData, ThrowOnError>,
|
options: Options<GetScheduleMirrorsData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).get<GetScheduleMirrorsResponses, unknown, ThrowOnError>({
|
(options.client ?? client).get<GetScheduleMirrorsResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}/mirrors",
|
url: "/api/v1/backups/{shortId}/mirrors",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -646,7 +646,7 @@ export const updateScheduleMirrors = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<UpdateScheduleMirrorsData, ThrowOnError>,
|
options: Options<UpdateScheduleMirrorsData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).put<UpdateScheduleMirrorsResponses, unknown, ThrowOnError>({
|
(options.client ?? client).put<UpdateScheduleMirrorsResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}/mirrors",
|
url: "/api/v1/backups/{shortId}/mirrors",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -661,12 +661,12 @@ export const getMirrorCompatibility = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<GetMirrorCompatibilityData, ThrowOnError>,
|
options: Options<GetMirrorCompatibilityData, ThrowOnError>,
|
||||||
) =>
|
) =>
|
||||||
(options.client ?? client).get<GetMirrorCompatibilityResponses, unknown, ThrowOnError>({
|
(options.client ?? client).get<GetMirrorCompatibilityResponses, unknown, ThrowOnError>({
|
||||||
url: "/api/v1/backups/{scheduleId}/mirrors/compatibility",
|
url: "/api/v1/backups/{shortId}/mirrors/compatibility",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reorder backup schedules by providing an array of schedule IDs in the desired order
|
* Reorder backup schedules by providing an array of schedule short IDs in the desired order
|
||||||
*/
|
*/
|
||||||
export const reorderBackupSchedules = <ThrowOnError extends boolean = false>(
|
export const reorderBackupSchedules = <ThrowOnError extends boolean = false>(
|
||||||
options?: Options<ReorderBackupSchedulesData, ThrowOnError>,
|
options?: Options<ReorderBackupSchedulesData, ThrowOnError>,
|
||||||
|
|
|
||||||
|
|
@ -346,10 +346,10 @@ export type TestConnectionResponse = TestConnectionResponses[keyof TestConnectio
|
||||||
export type DeleteVolumeData = {
|
export type DeleteVolumeData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/volumes/{id}";
|
url: "/api/v1/volumes/{shortId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeleteVolumeResponses = {
|
export type DeleteVolumeResponses = {
|
||||||
|
|
@ -366,10 +366,10 @@ export type DeleteVolumeResponse = DeleteVolumeResponses[keyof DeleteVolumeRespo
|
||||||
export type GetVolumeData = {
|
export type GetVolumeData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/volumes/{id}";
|
url: "/api/v1/volumes/{shortId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetVolumeErrors = {
|
export type GetVolumeErrors = {
|
||||||
|
|
@ -520,10 +520,10 @@ export type UpdateVolumeData = {
|
||||||
name?: string;
|
name?: string;
|
||||||
};
|
};
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/volumes/{id}";
|
url: "/api/v1/volumes/{shortId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateVolumeErrors = {
|
export type UpdateVolumeErrors = {
|
||||||
|
|
@ -610,10 +610,10 @@ export type UpdateVolumeResponse = UpdateVolumeResponses[keyof UpdateVolumeRespo
|
||||||
export type MountVolumeData = {
|
export type MountVolumeData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/volumes/{id}/mount";
|
url: "/api/v1/volumes/{shortId}/mount";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MountVolumeResponses = {
|
export type MountVolumeResponses = {
|
||||||
|
|
@ -631,10 +631,10 @@ export type MountVolumeResponse = MountVolumeResponses[keyof MountVolumeResponse
|
||||||
export type UnmountVolumeData = {
|
export type UnmountVolumeData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/volumes/{id}/unmount";
|
url: "/api/v1/volumes/{shortId}/unmount";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UnmountVolumeResponses = {
|
export type UnmountVolumeResponses = {
|
||||||
|
|
@ -652,10 +652,10 @@ export type UnmountVolumeResponse = UnmountVolumeResponses[keyof UnmountVolumeRe
|
||||||
export type HealthCheckVolumeData = {
|
export type HealthCheckVolumeData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/volumes/{id}/health-check";
|
url: "/api/v1/volumes/{shortId}/health-check";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HealthCheckVolumeErrors = {
|
export type HealthCheckVolumeErrors = {
|
||||||
|
|
@ -680,14 +680,14 @@ export type HealthCheckVolumeResponse = HealthCheckVolumeResponses[keyof HealthC
|
||||||
export type ListFilesData = {
|
export type ListFilesData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: {
|
query?: {
|
||||||
limit?: string;
|
limit?: string;
|
||||||
offset?: string;
|
offset?: string;
|
||||||
path?: string;
|
path?: string;
|
||||||
};
|
};
|
||||||
url: "/api/v1/volumes/{id}/files";
|
url: "/api/v1/volumes/{shortId}/files";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ListFilesResponses = {
|
export type ListFilesResponses = {
|
||||||
|
|
@ -1159,10 +1159,10 @@ export type ListRcloneRemotesResponse = ListRcloneRemotesResponses[keyof ListRcl
|
||||||
export type DeleteRepositoryData = {
|
export type DeleteRepositoryData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}";
|
url: "/api/v1/repositories/{shortId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeleteRepositoryResponses = {
|
export type DeleteRepositoryResponses = {
|
||||||
|
|
@ -1179,10 +1179,10 @@ export type DeleteRepositoryResponse = DeleteRepositoryResponses[keyof DeleteRep
|
||||||
export type GetRepositoryData = {
|
export type GetRepositoryData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}";
|
url: "/api/v1/repositories/{shortId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetRepositoryResponses = {
|
export type GetRepositoryResponses = {
|
||||||
|
|
@ -1553,10 +1553,10 @@ export type UpdateRepositoryData = {
|
||||||
name?: string;
|
name?: string;
|
||||||
};
|
};
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}";
|
url: "/api/v1/repositories/{shortId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateRepositoryErrors = {
|
export type UpdateRepositoryErrors = {
|
||||||
|
|
@ -1775,10 +1775,10 @@ export type DeleteSnapshotsData = {
|
||||||
snapshotIds: Array<string>;
|
snapshotIds: Array<string>;
|
||||||
};
|
};
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}/snapshots";
|
url: "/api/v1/repositories/{shortId}/snapshots";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeleteSnapshotsResponses = {
|
export type DeleteSnapshotsResponses = {
|
||||||
|
|
@ -1795,12 +1795,12 @@ export type DeleteSnapshotsResponse = DeleteSnapshotsResponses[keyof DeleteSnaps
|
||||||
export type ListSnapshotsData = {
|
export type ListSnapshotsData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: {
|
query?: {
|
||||||
backupId?: string;
|
backupId?: string;
|
||||||
};
|
};
|
||||||
url: "/api/v1/repositories/{id}/snapshots";
|
url: "/api/v1/repositories/{shortId}/snapshots";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ListSnapshotsResponses = {
|
export type ListSnapshotsResponses = {
|
||||||
|
|
@ -1840,10 +1840,10 @@ export type ListSnapshotsResponse = ListSnapshotsResponses[keyof ListSnapshotsRe
|
||||||
export type RefreshSnapshotsData = {
|
export type RefreshSnapshotsData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}/snapshots/refresh";
|
url: "/api/v1/repositories/{shortId}/snapshots/refresh";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RefreshSnapshotsResponses = {
|
export type RefreshSnapshotsResponses = {
|
||||||
|
|
@ -1861,11 +1861,11 @@ export type RefreshSnapshotsResponse = RefreshSnapshotsResponses[keyof RefreshSn
|
||||||
export type DeleteSnapshotData = {
|
export type DeleteSnapshotData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
snapshotId: string;
|
snapshotId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}/snapshots/{snapshotId}";
|
url: "/api/v1/repositories/{shortId}/snapshots/{snapshotId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeleteSnapshotResponses = {
|
export type DeleteSnapshotResponses = {
|
||||||
|
|
@ -1882,11 +1882,11 @@ export type DeleteSnapshotResponse = DeleteSnapshotResponses[keyof DeleteSnapsho
|
||||||
export type GetSnapshotDetailsData = {
|
export type GetSnapshotDetailsData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
snapshotId: string;
|
snapshotId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}/snapshots/{snapshotId}";
|
url: "/api/v1/repositories/{shortId}/snapshots/{snapshotId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetSnapshotDetailsResponses = {
|
export type GetSnapshotDetailsResponses = {
|
||||||
|
|
@ -1926,7 +1926,7 @@ export type GetSnapshotDetailsResponse = GetSnapshotDetailsResponses[keyof GetSn
|
||||||
export type ListSnapshotFilesData = {
|
export type ListSnapshotFilesData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
snapshotId: string;
|
snapshotId: string;
|
||||||
};
|
};
|
||||||
query?: {
|
query?: {
|
||||||
|
|
@ -1934,7 +1934,7 @@ export type ListSnapshotFilesData = {
|
||||||
offset?: string;
|
offset?: string;
|
||||||
path?: string;
|
path?: string;
|
||||||
};
|
};
|
||||||
url: "/api/v1/repositories/{id}/snapshots/{snapshotId}/files";
|
url: "/api/v1/repositories/{shortId}/snapshots/{snapshotId}/files";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ListSnapshotFilesResponses = {
|
export type ListSnapshotFilesResponses = {
|
||||||
|
|
@ -1981,10 +1981,10 @@ export type RestoreSnapshotData = {
|
||||||
targetPath?: string;
|
targetPath?: string;
|
||||||
};
|
};
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}/restore";
|
url: "/api/v1/repositories/{shortId}/restore";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RestoreSnapshotResponses = {
|
export type RestoreSnapshotResponses = {
|
||||||
|
|
@ -2004,10 +2004,10 @@ export type RestoreSnapshotResponse = RestoreSnapshotResponses[keyof RestoreSnap
|
||||||
export type CancelDoctorData = {
|
export type CancelDoctorData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}/doctor";
|
url: "/api/v1/repositories/{shortId}/doctor";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CancelDoctorErrors = {
|
export type CancelDoctorErrors = {
|
||||||
|
|
@ -2031,10 +2031,10 @@ export type CancelDoctorResponse = CancelDoctorResponses[keyof CancelDoctorRespo
|
||||||
export type StartDoctorData = {
|
export type StartDoctorData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}/doctor";
|
url: "/api/v1/repositories/{shortId}/doctor";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StartDoctorErrors = {
|
export type StartDoctorErrors = {
|
||||||
|
|
@ -2059,10 +2059,10 @@ export type StartDoctorResponse = StartDoctorResponses[keyof StartDoctorResponse
|
||||||
export type UnlockRepositoryData = {
|
export type UnlockRepositoryData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}/unlock";
|
url: "/api/v1/repositories/{shortId}/unlock";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UnlockRepositoryResponses = {
|
export type UnlockRepositoryResponses = {
|
||||||
|
|
@ -2085,10 +2085,10 @@ export type TagSnapshotsData = {
|
||||||
set?: Array<string>;
|
set?: Array<string>;
|
||||||
};
|
};
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}/snapshots/tag";
|
url: "/api/v1/repositories/{shortId}/snapshots/tag";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TagSnapshotsResponses = {
|
export type TagSnapshotsResponses = {
|
||||||
|
|
@ -2108,10 +2108,10 @@ export type DevPanelExecData = {
|
||||||
args?: Array<string>;
|
args?: Array<string>;
|
||||||
};
|
};
|
||||||
path: {
|
path: {
|
||||||
id: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/repositories/{id}/exec";
|
url: "/api/v1/repositories/{shortId}/exec";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DevPanelExecErrors = {
|
export type DevPanelExecErrors = {
|
||||||
|
|
@ -2433,7 +2433,7 @@ export type CreateBackupScheduleData = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
repositoryId: string;
|
repositoryId: string;
|
||||||
volumeId: number;
|
volumeId: number | string;
|
||||||
excludeIfPresent?: Array<string>;
|
excludeIfPresent?: Array<string>;
|
||||||
excludePatterns?: Array<string>;
|
excludePatterns?: Array<string>;
|
||||||
includePatterns?: Array<string>;
|
includePatterns?: Array<string>;
|
||||||
|
|
@ -2493,10 +2493,10 @@ export type CreateBackupScheduleResponse = CreateBackupScheduleResponses[keyof C
|
||||||
export type DeleteBackupScheduleData = {
|
export type DeleteBackupScheduleData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}";
|
url: "/api/v1/backups/{shortId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeleteBackupScheduleResponses = {
|
export type DeleteBackupScheduleResponses = {
|
||||||
|
|
@ -2513,10 +2513,10 @@ export type DeleteBackupScheduleResponse = DeleteBackupScheduleResponses[keyof D
|
||||||
export type GetBackupScheduleData = {
|
export type GetBackupScheduleData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}";
|
url: "/api/v1/backups/{shortId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetBackupScheduleResponses = {
|
export type GetBackupScheduleResponses = {
|
||||||
|
|
@ -2831,10 +2831,10 @@ export type UpdateBackupScheduleData = {
|
||||||
tags?: Array<string>;
|
tags?: Array<string>;
|
||||||
};
|
};
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}";
|
url: "/api/v1/backups/{shortId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateBackupScheduleResponses = {
|
export type UpdateBackupScheduleResponses = {
|
||||||
|
|
@ -2876,10 +2876,10 @@ export type UpdateBackupScheduleResponse = UpdateBackupScheduleResponses[keyof U
|
||||||
export type GetBackupScheduleForVolumeData = {
|
export type GetBackupScheduleForVolumeData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
volumeId: string;
|
volumeShortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/volume/{volumeId}";
|
url: "/api/v1/backups/volume/{volumeShortId}";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetBackupScheduleForVolumeResponses = {
|
export type GetBackupScheduleForVolumeResponses = {
|
||||||
|
|
@ -3176,10 +3176,10 @@ export type GetBackupScheduleForVolumeResponse =
|
||||||
export type RunBackupNowData = {
|
export type RunBackupNowData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}/run";
|
url: "/api/v1/backups/{shortId}/run";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RunBackupNowResponses = {
|
export type RunBackupNowResponses = {
|
||||||
|
|
@ -3196,10 +3196,10 @@ export type RunBackupNowResponse = RunBackupNowResponses[keyof RunBackupNowRespo
|
||||||
export type StopBackupData = {
|
export type StopBackupData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}/stop";
|
url: "/api/v1/backups/{shortId}/stop";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StopBackupErrors = {
|
export type StopBackupErrors = {
|
||||||
|
|
@ -3223,10 +3223,10 @@ export type StopBackupResponse = StopBackupResponses[keyof StopBackupResponses];
|
||||||
export type RunForgetData = {
|
export type RunForgetData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}/forget";
|
url: "/api/v1/backups/{shortId}/forget";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RunForgetResponses = {
|
export type RunForgetResponses = {
|
||||||
|
|
@ -3243,10 +3243,10 @@ export type RunForgetResponse = RunForgetResponses[keyof RunForgetResponses];
|
||||||
export type GetScheduleNotificationsData = {
|
export type GetScheduleNotificationsData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}/notifications";
|
url: "/api/v1/backups/{shortId}/notifications";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetScheduleNotificationsResponses = {
|
export type GetScheduleNotificationsResponses = {
|
||||||
|
|
@ -3355,10 +3355,10 @@ export type UpdateScheduleNotificationsData = {
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}/notifications";
|
url: "/api/v1/backups/{shortId}/notifications";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateScheduleNotificationsResponses = {
|
export type UpdateScheduleNotificationsResponses = {
|
||||||
|
|
@ -3459,10 +3459,10 @@ export type UpdateScheduleNotificationsResponse =
|
||||||
export type GetScheduleMirrorsData = {
|
export type GetScheduleMirrorsData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}/mirrors";
|
url: "/api/v1/backups/{shortId}/mirrors";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetScheduleMirrorsResponses = {
|
export type GetScheduleMirrorsResponses = {
|
||||||
|
|
@ -3664,7 +3664,7 @@ export type GetScheduleMirrorsResponses = {
|
||||||
updatedAt: number;
|
updatedAt: number;
|
||||||
};
|
};
|
||||||
repositoryId: string;
|
repositoryId: string;
|
||||||
scheduleId: number;
|
scheduleId: string;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -3678,10 +3678,10 @@ export type UpdateScheduleMirrorsData = {
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}/mirrors";
|
url: "/api/v1/backups/{shortId}/mirrors";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateScheduleMirrorsResponses = {
|
export type UpdateScheduleMirrorsResponses = {
|
||||||
|
|
@ -3883,7 +3883,7 @@ export type UpdateScheduleMirrorsResponses = {
|
||||||
updatedAt: number;
|
updatedAt: number;
|
||||||
};
|
};
|
||||||
repositoryId: string;
|
repositoryId: string;
|
||||||
scheduleId: number;
|
scheduleId: string;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -3892,10 +3892,10 @@ export type UpdateScheduleMirrorsResponse = UpdateScheduleMirrorsResponses[keyof
|
||||||
export type GetMirrorCompatibilityData = {
|
export type GetMirrorCompatibilityData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
scheduleId: string;
|
shortId: string;
|
||||||
};
|
};
|
||||||
query?: never;
|
query?: never;
|
||||||
url: "/api/v1/backups/{scheduleId}/mirrors/compatibility";
|
url: "/api/v1/backups/{shortId}/mirrors/compatibility";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetMirrorCompatibilityResponses = {
|
export type GetMirrorCompatibilityResponses = {
|
||||||
|
|
@ -3913,7 +3913,7 @@ export type GetMirrorCompatibilityResponse = GetMirrorCompatibilityResponses[key
|
||||||
|
|
||||||
export type ReorderBackupSchedulesData = {
|
export type ReorderBackupSchedulesData = {
|
||||||
body?: {
|
body?: {
|
||||||
scheduleIds: Array<number>;
|
scheduleShortIds: Array<string>;
|
||||||
};
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ export function DevPanel({ open, onOpenChange }: DevPanelProps) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await devPanelExec({
|
const result = await devPanelExec({
|
||||||
path: { id: selectedRepoId },
|
path: { shortId: selectedRepoId },
|
||||||
body: { command, args: argsArray.length > 0 ? argsArray : undefined },
|
body: { command, args: argsArray.length > 0 ? argsArray : undefined },
|
||||||
signal: abortControllerRef.current.signal,
|
signal: abortControllerRef.current.signal,
|
||||||
});
|
});
|
||||||
|
|
@ -155,7 +155,7 @@ export function DevPanel({ open, onOpenChange }: DevPanelProps) {
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{repositories.map((repo) => (
|
{repositories.map((repo) => (
|
||||||
<SelectItem key={repo.id} value={repo.id}>
|
<SelectItem key={repo.id} value={repo.shortId}>
|
||||||
{repo.name} ({repo.type})
|
{repo.name} ({repo.type})
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export const SnapshotTreeBrowser = ({
|
||||||
|
|
||||||
const { data, isLoading, error } = useQuery({
|
const { data, isLoading, error } = useQuery({
|
||||||
...listSnapshotFilesOptions({
|
...listSnapshotFilesOptions({
|
||||||
path: { id: repositoryId, snapshotId },
|
path: { shortId: repositoryId, snapshotId },
|
||||||
query: { path: normalizedBasePath },
|
query: { path: normalizedBasePath },
|
||||||
}),
|
}),
|
||||||
enabled,
|
enabled,
|
||||||
|
|
@ -92,7 +92,7 @@ export const SnapshotTreeBrowser = ({
|
||||||
fetchFolder: async (path, offset = 0) => {
|
fetchFolder: async (path, offset = 0) => {
|
||||||
return await queryClient.ensureQueryData(
|
return await queryClient.ensureQueryData(
|
||||||
listSnapshotFilesOptions({
|
listSnapshotFilesOptions({
|
||||||
path: { id: repositoryId, snapshotId },
|
path: { shortId: repositoryId, snapshotId },
|
||||||
query: {
|
query: {
|
||||||
path,
|
path,
|
||||||
offset: offset.toString(),
|
offset: offset.toString(),
|
||||||
|
|
@ -104,7 +104,7 @@ export const SnapshotTreeBrowser = ({
|
||||||
prefetchFolder: (path) => {
|
prefetchFolder: (path) => {
|
||||||
void queryClient.prefetchQuery(
|
void queryClient.prefetchQuery(
|
||||||
listSnapshotFilesOptions({
|
listSnapshotFilesOptions({
|
||||||
path: { id: repositoryId, snapshotId },
|
path: { shortId: repositoryId, snapshotId },
|
||||||
query: {
|
query: {
|
||||||
path,
|
path,
|
||||||
offset: "0",
|
offset: "0",
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export const VolumeFileBrowser = ({ volumeId, enabled = true, ...uiProps }: Volu
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const { data, isLoading, error } = useQuery({
|
const { data, isLoading, error } = useQuery({
|
||||||
...listFilesOptions({ path: { id: volumeId } }),
|
...listFilesOptions({ path: { shortId: volumeId } }),
|
||||||
enabled,
|
enabled,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ export const VolumeFileBrowser = ({ volumeId, enabled = true, ...uiProps }: Volu
|
||||||
fetchFolder: async (path, offset): Promise<FetchFolderResult> => {
|
fetchFolder: async (path, offset): Promise<FetchFolderResult> => {
|
||||||
return await queryClient.ensureQueryData(
|
return await queryClient.ensureQueryData(
|
||||||
listFilesOptions({
|
listFilesOptions({
|
||||||
path: { id: volumeId },
|
path: { shortId: volumeId },
|
||||||
query: { path, offset: offset?.toString() },
|
query: { path, offset: offset?.toString() },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
@ -31,7 +31,7 @@ export const VolumeFileBrowser = ({ volumeId, enabled = true, ...uiProps }: Volu
|
||||||
prefetchFolder: (path) => {
|
prefetchFolder: (path) => {
|
||||||
void queryClient.prefetchQuery(
|
void queryClient.prefetchQuery(
|
||||||
listFilesOptions({
|
listFilesOptions({
|
||||||
path: { id: volumeId },
|
path: { shortId: volumeId },
|
||||||
query: { path },
|
query: { path },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ export function RestoreForm({ repository, snapshotId, returnPath, basePath }: Re
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
restoreCompletedRef.current = true;
|
restoreCompletedRef.current = true;
|
||||||
setIsRestoreActive(false);
|
setIsRestoreActive(false);
|
||||||
handleRepositoryError("Restore failed", error, repository.id);
|
handleRepositoryError("Restore failed", error, repository.shortId);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -113,7 +113,7 @@ export function RestoreForm({ repository, snapshotId, returnPath, basePath }: Re
|
||||||
setShowRestoreResultAlert(false);
|
setShowRestoreResultAlert(false);
|
||||||
|
|
||||||
restoreSnapshot({
|
restoreSnapshot({
|
||||||
path: { id: repository.id },
|
path: { shortId: repository.shortId },
|
||||||
body: {
|
body: {
|
||||||
snapshotId,
|
snapshotId,
|
||||||
include: includePaths.length > 0 ? includePaths : undefined,
|
include: includePaths.length > 0 ? includePaths : undefined,
|
||||||
|
|
@ -123,7 +123,7 @@ export function RestoreForm({ repository, snapshotId, returnPath, basePath }: Re
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
repository.id,
|
repository.shortId,
|
||||||
snapshotId,
|
snapshotId,
|
||||||
excludeXattr,
|
excludeXattr,
|
||||||
restoreLocation,
|
restoreLocation,
|
||||||
|
|
@ -280,7 +280,7 @@ export function RestoreForm({ repository, snapshotId, returnPath, basePath }: Re
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex-1 overflow-hidden flex flex-col p-0">
|
<CardContent className="flex-1 overflow-hidden flex flex-col p-0">
|
||||||
<SnapshotTreeBrowser
|
<SnapshotTreeBrowser
|
||||||
repositoryId={repository.id}
|
repositoryId={repository.shortId}
|
||||||
snapshotId={snapshotId}
|
snapshotId={snapshotId}
|
||||||
basePath={volumeBasePath}
|
basePath={volumeBasePath}
|
||||||
pageSize={500}
|
pageSize={500}
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ export const SnapshotsTable = ({ snapshots, repositoryId, backups, listSnapshots
|
||||||
const handleBulkDelete = () => {
|
const handleBulkDelete = () => {
|
||||||
toast.promise(
|
toast.promise(
|
||||||
deleteSnapshots.mutateAsync({
|
deleteSnapshots.mutateAsync({
|
||||||
path: { id: repositoryId },
|
path: { shortId: repositoryId },
|
||||||
body: { snapshotIds: Array.from(selectedIds) },
|
body: { snapshotIds: Array.from(selectedIds) },
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
|
@ -111,12 +111,12 @@ export const SnapshotsTable = ({ snapshots, repositoryId, backups, listSnapshots
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBulkReTag = () => {
|
const handleBulkReTag = () => {
|
||||||
const schedule = backups.find((b) => String(b.id) === targetScheduleId);
|
const schedule = backups.find((b) => b.shortId === targetScheduleId);
|
||||||
if (!schedule) return;
|
if (!schedule) return;
|
||||||
|
|
||||||
toast.promise(
|
toast.promise(
|
||||||
tagSnapshots.mutateAsync({
|
tagSnapshots.mutateAsync({
|
||||||
path: { id: repositoryId },
|
path: { shortId: repositoryId },
|
||||||
body: {
|
body: {
|
||||||
snapshotIds: Array.from(selectedIds),
|
snapshotIds: Array.from(selectedIds),
|
||||||
set: [schedule.shortId],
|
set: [schedule.shortId],
|
||||||
|
|
@ -187,7 +187,7 @@ export const SnapshotsTable = ({ snapshots, repositoryId, backups, listSnapshots
|
||||||
<Link
|
<Link
|
||||||
hidden={!backup}
|
hidden={!backup}
|
||||||
to={backup ? `/backups/$backupId` : "."}
|
to={backup ? `/backups/$backupId` : "."}
|
||||||
params={backup ? { backupId: String(backup.id) } : {}}
|
params={backup ? { backupId: backup.shortId } : {}}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
className="hover:underline"
|
className="hover:underline"
|
||||||
>
|
>
|
||||||
|
|
@ -301,7 +301,7 @@ export const SnapshotsTable = ({ snapshots, repositoryId, backups, listSnapshots
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{backups.map((backup) => (
|
{backups.map((backup) => (
|
||||||
<SelectItem key={backup.id} value={String(backup.id)}>
|
<SelectItem key={backup.shortId} value={backup.shortId}>
|
||||||
{backup.name}
|
{backup.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import type { PropsWithChildren } from "react";
|
||||||
|
|
||||||
interface SortableBackupCardProps {
|
interface SortableBackupCardProps {
|
||||||
isDragging?: boolean;
|
isDragging?: boolean;
|
||||||
uniqueId: number;
|
uniqueId: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SortableCard({ isDragging, uniqueId, children }: PropsWithChildren<SortableBackupCardProps>) {
|
export function SortableCard({ isDragging, uniqueId, children }: PropsWithChildren<SortableBackupCardProps>) {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export const showLockErrorToast = (repositoryId: string, title: string) => {
|
||||||
toast.promise(
|
toast.promise(
|
||||||
async () => {
|
async () => {
|
||||||
const result = await unlockRepository({
|
const result = await unlockRepository({
|
||||||
path: { id: repositoryId },
|
path: { shortId: repositoryId },
|
||||||
throwOnError: true,
|
throwOnError: true,
|
||||||
});
|
});
|
||||||
return result.data;
|
return result.data;
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import { Link } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const BackupCard = ({ schedule }: { schedule: BackupSchedule }) => {
|
export const BackupCard = ({ schedule }: { schedule: BackupSchedule }) => {
|
||||||
return (
|
return (
|
||||||
<Link key={schedule.id} to="/backups/$backupId" params={{ backupId: schedule.id.toString() }}>
|
<Link key={schedule.shortId} to="/backups/$backupId" params={{ backupId: schedule.shortId }}>
|
||||||
<Card key={schedule.id} className="flex flex-col h-full">
|
<Card key={schedule.shortId} className="flex flex-col h-full">
|
||||||
<CardHeader className="pb-3 overflow-hidden">
|
<CardHeader className="pb-3 overflow-hidden">
|
||||||
<div className="flex items-center justify-between gap-2 w-full">
|
<div className="flex items-center justify-between gap-2 w-full">
|
||||||
<div className="flex items-center gap-2 flex-1 min-w-0 w-0">
|
<div className="flex items-center gap-2 flex-1 min-w-0 w-0">
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,22 @@ import { formatDuration } from "~/utils/utils";
|
||||||
import { formatBytes } from "~/utils/format-bytes";
|
import { formatBytes } from "~/utils/format-bytes";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
scheduleId: number;
|
scheduleShortId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BackupProgressCard = ({ scheduleId }: Props) => {
|
export const BackupProgressCard = ({ scheduleShortId }: Props) => {
|
||||||
const { addEventListener } = useServerEvents();
|
const { addEventListener } = useServerEvents();
|
||||||
const [progress, setProgress] = useState<BackupProgressEvent | null>(null);
|
const [progress, setProgress] = useState<BackupProgressEvent | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = addEventListener("backup:progress", (progressData) => {
|
const unsubscribe = addEventListener("backup:progress", (progressData) => {
|
||||||
if (progressData.scheduleId === scheduleId) {
|
if (progressData.scheduleId === scheduleShortId) {
|
||||||
setProgress(progressData);
|
setProgress(progressData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const unsubscribeComplete = addEventListener("backup:completed", (completedData) => {
|
const unsubscribeComplete = addEventListener("backup:completed", (completedData) => {
|
||||||
if (completedData.scheduleId === scheduleId) {
|
if (completedData.scheduleId === scheduleShortId) {
|
||||||
setProgress(null);
|
setProgress(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -31,7 +31,7 @@ export const BackupProgressCard = ({ scheduleId }: Props) => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
unsubscribeComplete();
|
unsubscribeComplete();
|
||||||
};
|
};
|
||||||
}, [addEventListener, scheduleId]);
|
}, [addEventListener, scheduleShortId]);
|
||||||
|
|
||||||
const percentDone = progress ? Math.round(progress.percent_done * 100) : 0;
|
const percentDone = progress ? Math.round(progress.percent_done * 100) : 0;
|
||||||
const currentFile = progress?.current_files[0] || "";
|
const currentFile = progress?.current_files[0] || "";
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ export const BasicInfoSection = ({ form, volume }: BasicInfoSectionProps) => {
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{repositoriesData?.map((repo) => (
|
{repositoriesData?.map((repo) => (
|
||||||
<SelectItem key={repo.id} value={repo.id}>
|
<SelectItem key={repo.shortId} value={repo.shortId}>
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<RepositoryIcon backend={repo.type} />
|
<RepositoryIcon backend={repo.type} />
|
||||||
{repo.name}
|
{repo.name}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@ export const SummarySection = ({ volume, frequency, formValues }: SummarySection
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs uppercase text-muted-foreground">Repository</p>
|
<p className="text-xs uppercase text-muted-foreground">Repository</p>
|
||||||
<p className="font-medium">{repositoriesData?.find((r) => r.id === formValues.repositoryId)?.name || "—"}</p>
|
<p className="font-medium">
|
||||||
|
{repositoriesData?.find((r) => r.shortId === formValues.repositoryId)?.name || "—"}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{(formValues.includePatterns && formValues.includePatterns.length > 0) || formValues.includePatternsText ? (
|
{(formValues.includePatterns && formValues.includePatterns.length > 0) || formValues.includePatternsText ? (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@ import type { BackupSchedule } from "~/client/lib/types";
|
||||||
import { cronToFormValues } from "../../lib/cron-utils";
|
import { cronToFormValues } from "../../lib/cron-utils";
|
||||||
import type { InternalFormValues } from "./types";
|
import type { InternalFormValues } from "./types";
|
||||||
|
|
||||||
export const backupScheduleToFormValues = (
|
export const backupScheduleToFormValues = (schedule?: BackupSchedule): InternalFormValues | undefined => {
|
||||||
schedule?: BackupSchedule,
|
|
||||||
): InternalFormValues | undefined => {
|
|
||||||
if (!schedule) {
|
if (!schedule) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +16,7 @@ export const backupScheduleToFormValues = (
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: schedule.name,
|
name: schedule.name,
|
||||||
repositoryId: schedule.repositoryId,
|
repositoryId: schedule.repository.shortId,
|
||||||
includePatterns: fileBrowserPaths.length > 0 ? fileBrowserPaths : undefined,
|
includePatterns: fileBrowserPaths.length > 0 ? fileBrowserPaths : undefined,
|
||||||
includePatternsText: textPatterns.length > 0 ? textPatterns.join("\n") : undefined,
|
includePatternsText: textPatterns.length > 0 ? textPatterns.join("\n") : undefined,
|
||||||
excludePatternsText: schedule.excludePatterns?.join("\n") || undefined,
|
excludePatternsText: schedule.excludePatterns?.join("\n") || undefined,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import type { GetScheduleMirrorsResponse } from "~/client/api-client";
|
||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
scheduleId: number;
|
scheduleShortId: string;
|
||||||
primaryRepositoryId: string;
|
primaryRepositoryId: string;
|
||||||
repositories: Repository[];
|
repositories: Repository[];
|
||||||
initialData: GetScheduleMirrorsResponse;
|
initialData: GetScheduleMirrorsResponse;
|
||||||
|
|
@ -55,14 +55,14 @@ const buildAssignments = (mirrors: GetScheduleMirrorsResponse) =>
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, repositories, initialData }: Props) => {
|
export const ScheduleMirrorsConfig = ({ scheduleShortId, primaryRepositoryId, repositories, initialData }: Props) => {
|
||||||
const [assignments, setAssignments] = useState<Map<string, MirrorAssignment>>(() => buildAssignments(initialData));
|
const [assignments, setAssignments] = useState<Map<string, MirrorAssignment>>(() => buildAssignments(initialData));
|
||||||
const [hasChanges, setHasChanges] = useState(false);
|
const [hasChanges, setHasChanges] = useState(false);
|
||||||
const [isAddingNew, setIsAddingNew] = useState(false);
|
const [isAddingNew, setIsAddingNew] = useState(false);
|
||||||
const { addEventListener } = useServerEvents();
|
const { addEventListener } = useServerEvents();
|
||||||
|
|
||||||
const { data: currentMirrors } = useSuspenseQuery({
|
const { data: currentMirrors } = useSuspenseQuery({
|
||||||
...getScheduleMirrorsOptions({ path: { scheduleId: scheduleId.toString() } }),
|
...getScheduleMirrorsOptions({ path: { shortId: scheduleShortId } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -72,7 +72,7 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
||||||
}, [currentMirrors, hasChanges]);
|
}, [currentMirrors, hasChanges]);
|
||||||
|
|
||||||
const { data: compatibility } = useQuery({
|
const { data: compatibility } = useQuery({
|
||||||
...getMirrorCompatibilityOptions({ path: { scheduleId: scheduleId.toString() } }),
|
...getMirrorCompatibilityOptions({ path: { shortId: scheduleShortId } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateMirrors = useMutation({
|
const updateMirrors = useMutation({
|
||||||
|
|
@ -100,7 +100,7 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribeStarted = addEventListener("mirror:started", (event) => {
|
const unsubscribeStarted = addEventListener("mirror:started", (event) => {
|
||||||
if (event.scheduleId !== scheduleId) return;
|
if (event.scheduleId !== scheduleShortId) return;
|
||||||
setAssignments((prev) => {
|
setAssignments((prev) => {
|
||||||
const next = new Map(prev);
|
const next = new Map(prev);
|
||||||
const existing = next.get(event.repositoryId);
|
const existing = next.get(event.repositoryId);
|
||||||
|
|
@ -111,7 +111,7 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
||||||
});
|
});
|
||||||
|
|
||||||
const unsubscribeCompleted = addEventListener("mirror:completed", (event) => {
|
const unsubscribeCompleted = addEventListener("mirror:completed", (event) => {
|
||||||
if (event.scheduleId !== scheduleId) return;
|
if (event.scheduleId !== scheduleShortId) return;
|
||||||
setAssignments((prev) => {
|
setAssignments((prev) => {
|
||||||
const next = new Map(prev);
|
const next = new Map(prev);
|
||||||
const existing = next.get(event.repositoryId);
|
const existing = next.get(event.repositoryId);
|
||||||
|
|
@ -130,7 +130,7 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
||||||
unsubscribeStarted();
|
unsubscribeStarted();
|
||||||
unsubscribeCompleted();
|
unsubscribeCompleted();
|
||||||
};
|
};
|
||||||
}, [addEventListener, scheduleId]);
|
}, [addEventListener, scheduleShortId]);
|
||||||
|
|
||||||
const addRepository = (repositoryId: string) => {
|
const addRepository = (repositoryId: string) => {
|
||||||
const newAssignments = new Map(assignments);
|
const newAssignments = new Map(assignments);
|
||||||
|
|
@ -174,7 +174,7 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
||||||
enabled: a.enabled,
|
enabled: a.enabled,
|
||||||
}));
|
}));
|
||||||
updateMirrors.mutate({
|
updateMirrors.mutate({
|
||||||
path: { scheduleId: scheduleId.toString() },
|
path: { shortId: scheduleShortId },
|
||||||
body: {
|
body: {
|
||||||
mirrors: mirrorsList,
|
mirrors: mirrorsList,
|
||||||
},
|
},
|
||||||
|
|
@ -188,18 +188,18 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
||||||
|
|
||||||
const selectableRepositories =
|
const selectableRepositories =
|
||||||
repositories?.filter((r) => {
|
repositories?.filter((r) => {
|
||||||
if (r.id === primaryRepositoryId) return false;
|
if (r.shortId === primaryRepositoryId) return false;
|
||||||
if (assignments.has(r.id)) return false;
|
if (assignments.has(r.shortId)) return false;
|
||||||
return true;
|
return true;
|
||||||
}) || [];
|
}) || [];
|
||||||
|
|
||||||
const hasAvailableRepositories = selectableRepositories.some((r) => {
|
const hasAvailableRepositories = selectableRepositories.some((r) => {
|
||||||
const compat = compatibilityMap.get(r.id);
|
const compat = compatibilityMap.get(r.shortId);
|
||||||
return compat?.compatible !== false;
|
return compat?.compatible !== false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const assignedRepositories = Array.from(assignments.keys())
|
const assignedRepositories = Array.from(assignments.keys())
|
||||||
.map((id) => repositories?.find((r) => r.id === id))
|
.map((id) => repositories?.find((r) => r.shortId === id))
|
||||||
.filter((r) => r !== undefined);
|
.filter((r) => r !== undefined);
|
||||||
|
|
||||||
const getStatusVariant = (status: string | null) => {
|
const getStatusVariant = (status: string | null) => {
|
||||||
|
|
@ -262,13 +262,13 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{selectableRepositories.map((repository) => {
|
{selectableRepositories.map((repository) => {
|
||||||
const compat = compatibilityMap.get(repository.id);
|
const compat = compatibilityMap.get(repository.shortId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip key={repository.id}>
|
<Tooltip key={repository.shortId}>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div>
|
<div>
|
||||||
<SelectItem value={repository.id} disabled={!compat?.compatible}>
|
<SelectItem value={repository.shortId} disabled={!compat?.compatible}>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RepositoryIcon backend={repository.type} className="h-4 w-4" />
|
<RepositoryIcon backend={repository.type} className="h-4 w-4" />
|
||||||
<span>{repository.name}</span>
|
<span>{repository.name}</span>
|
||||||
|
|
@ -322,16 +322,16 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{assignedRepositories.map((repository) => {
|
{assignedRepositories.map((repository) => {
|
||||||
const assignment = assignments.get(repository.id);
|
const assignment = assignments.get(repository.shortId);
|
||||||
if (!assignment) return null;
|
if (!assignment) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow key={repository.id}>
|
<TableRow key={repository.shortId}>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Link
|
<Link
|
||||||
to="/repositories/$repositoryId"
|
to="/repositories/$repositoryId"
|
||||||
params={{ repositoryId: repository.id }}
|
params={{ repositoryId: repository.shortId }}
|
||||||
className="hover:underline flex items-center gap-2"
|
className="hover:underline flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<RepositoryIcon backend={repository.type} className="h-4 w-4" />
|
<RepositoryIcon backend={repository.type} className="h-4 w-4" />
|
||||||
|
|
@ -346,7 +346,7 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
||||||
<Switch
|
<Switch
|
||||||
className="align-middle"
|
className="align-middle"
|
||||||
checked={assignment.enabled}
|
checked={assignment.enabled}
|
||||||
onCheckedChange={() => toggleEnabled(repository.id)}
|
onCheckedChange={() => toggleEnabled(repository.shortId)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
|
@ -363,7 +363,7 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => removeRepository(repository.id)}
|
onClick={() => removeRepository(repository.shortId)}
|
||||||
className="h-8 w-8 text-muted-foreground hover:text-destructive align-baseline"
|
className="h-8 w-8 text-muted-foreground hover:text-destructive align-baseline"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-4 w-4" />
|
<Trash2 className="h-4 w-4" />
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import type { NotificationDestination } from "~/client/lib/types";
|
||||||
import type { GetScheduleNotificationsResponse } from "~/client/api-client";
|
import type { GetScheduleNotificationsResponse } from "~/client/api-client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
scheduleId: number;
|
scheduleShortId: string;
|
||||||
destinations: NotificationDestination[];
|
destinations: NotificationDestination[];
|
||||||
initialData: GetScheduleNotificationsResponse;
|
initialData: GetScheduleNotificationsResponse;
|
||||||
};
|
};
|
||||||
|
|
@ -30,7 +30,7 @@ type NotificationAssignment = {
|
||||||
notifyOnFailure: boolean;
|
notifyOnFailure: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ScheduleNotificationsConfig = ({ scheduleId, destinations, initialData }: Props) => {
|
export const ScheduleNotificationsConfig = ({ scheduleShortId, destinations, initialData }: Props) => {
|
||||||
const map = new Map<number, NotificationAssignment>();
|
const map = new Map<number, NotificationAssignment>();
|
||||||
for (const assignment of initialData) {
|
for (const assignment of initialData) {
|
||||||
map.set(assignment.destinationId, {
|
map.set(assignment.destinationId, {
|
||||||
|
|
@ -47,7 +47,7 @@ export const ScheduleNotificationsConfig = ({ scheduleId, destinations, initialD
|
||||||
const [isAddingNew, setIsAddingNew] = useState(false);
|
const [isAddingNew, setIsAddingNew] = useState(false);
|
||||||
|
|
||||||
const { data: currentAssignments } = useQuery({
|
const { data: currentAssignments } = useQuery({
|
||||||
...getScheduleNotificationsOptions({ path: { scheduleId: scheduleId.toString() } }),
|
...getScheduleNotificationsOptions({ path: { shortId: scheduleShortId } }),
|
||||||
initialData,
|
initialData,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -107,7 +107,7 @@ export const ScheduleNotificationsConfig = ({ scheduleId, destinations, initialD
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const assignmentsList = Array.from(assignments.values());
|
const assignmentsList = Array.from(assignments.values());
|
||||||
updateNotifications.mutate({
|
updateNotifications.mutate({
|
||||||
path: { scheduleId: scheduleId.toString() },
|
path: { shortId: scheduleShortId },
|
||||||
body: {
|
body: {
|
||||||
assignments: assignmentsList,
|
assignments: assignmentsList,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ export const ScheduleSummary = (props: Props) => {
|
||||||
|
|
||||||
const handleConfirmForget = () => {
|
const handleConfirmForget = () => {
|
||||||
setShowForgetConfirm(false);
|
setShowForgetConfirm(false);
|
||||||
runForget.mutate({ path: { scheduleId: schedule.id.toString() } });
|
runForget.mutate({ path: { shortId: schedule.shortId } });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfirmStop = () => {
|
const handleConfirmStop = () => {
|
||||||
|
|
@ -215,7 +215,7 @@ export const ScheduleSummary = (props: Props) => {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{schedule.lastBackupStatus === "in_progress" && <BackupProgressCard scheduleId={schedule.id} />}
|
{schedule.lastBackupStatus === "in_progress" && <BackupProgressCard scheduleShortId={schedule.shortId} />}
|
||||||
|
|
||||||
<AlertDialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
<AlertDialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ interface ScheduleAwareTreeBrowserProps {
|
||||||
|
|
||||||
const ScheduleAwareTreeBrowser = ({ scheduleShortId, repositoryId, snapshotId }: ScheduleAwareTreeBrowserProps) => {
|
const ScheduleAwareTreeBrowser = ({ scheduleShortId, repositoryId, snapshotId }: ScheduleAwareTreeBrowserProps) => {
|
||||||
const { data: schedule, isPending } = useQuery({
|
const { data: schedule, isPending } = useQuery({
|
||||||
...getBackupScheduleOptions({ path: { scheduleId: scheduleShortId } }),
|
...getBackupScheduleOptions({ path: { shortId: scheduleShortId } }),
|
||||||
retry: false,
|
retry: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export function ScheduleDetailsPage(props: Props) {
|
||||||
const [snapshotToDelete, setSnapshotToDelete] = useState<string | null>(null);
|
const [snapshotToDelete, setSnapshotToDelete] = useState<string | null>(null);
|
||||||
|
|
||||||
const { data: schedule } = useSuspenseQuery({
|
const { data: schedule } = useSuspenseQuery({
|
||||||
...getBackupScheduleOptions({ path: { scheduleId } }),
|
...getBackupScheduleOptions({ path: { shortId: scheduleId } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -77,7 +77,7 @@ export function ScheduleDetailsPage(props: Props) {
|
||||||
isLoading,
|
isLoading,
|
||||||
failureReason,
|
failureReason,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
...listSnapshotsOptions({ path: { id: schedule.repository.id }, query: { backupId: schedule.shortId } }),
|
...listSnapshotsOptions({ path: { shortId: schedule.repository.shortId }, query: { backupId: schedule.shortId } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateSchedule = useMutation({
|
const updateSchedule = useMutation({
|
||||||
|
|
@ -124,7 +124,10 @@ export function ScheduleDetailsPage(props: Props) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const listSnapshotsQueryOptions = { path: { id: schedule.repository.id }, query: { backupId: schedule.shortId } };
|
const listSnapshotsQueryOptions = {
|
||||||
|
path: { shortId: schedule.repository.shortId },
|
||||||
|
query: { backupId: schedule.shortId },
|
||||||
|
};
|
||||||
|
|
||||||
const deleteSnapshot = useMutation({
|
const deleteSnapshot = useMutation({
|
||||||
...deleteSnapshotMutation(),
|
...deleteSnapshotMutation(),
|
||||||
|
|
@ -167,7 +170,7 @@ export function ScheduleDetailsPage(props: Props) {
|
||||||
if (formValues.keepYearly) retentionPolicy.keepYearly = formValues.keepYearly;
|
if (formValues.keepYearly) retentionPolicy.keepYearly = formValues.keepYearly;
|
||||||
|
|
||||||
updateSchedule.mutate({
|
updateSchedule.mutate({
|
||||||
path: { scheduleId: schedule.id.toString() },
|
path: { shortId: schedule.shortId },
|
||||||
body: {
|
body: {
|
||||||
name: formValues.name,
|
name: formValues.name,
|
||||||
repositoryId: formValues.repositoryId,
|
repositoryId: formValues.repositoryId,
|
||||||
|
|
@ -184,7 +187,7 @@ export function ScheduleDetailsPage(props: Props) {
|
||||||
|
|
||||||
const handleToggleEnabled = (enabled: boolean) => {
|
const handleToggleEnabled = (enabled: boolean) => {
|
||||||
updateSchedule.mutate({
|
updateSchedule.mutate({
|
||||||
path: { scheduleId: schedule.id.toString() },
|
path: { shortId: schedule.shortId },
|
||||||
body: {
|
body: {
|
||||||
repositoryId: schedule.repositoryId,
|
repositoryId: schedule.repositoryId,
|
||||||
enabled,
|
enabled,
|
||||||
|
|
@ -207,7 +210,7 @@ export function ScheduleDetailsPage(props: Props) {
|
||||||
if (snapshotToDelete) {
|
if (snapshotToDelete) {
|
||||||
toast.promise(
|
toast.promise(
|
||||||
deleteSnapshot.mutateAsync({
|
deleteSnapshot.mutateAsync({
|
||||||
path: { id: schedule.repository.shortId, snapshotId: snapshotToDelete },
|
path: { shortId: schedule.repository.shortId, snapshotId: snapshotToDelete },
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
loading: "Deleting snapshot...",
|
loading: "Deleting snapshot...",
|
||||||
|
|
@ -251,23 +254,23 @@ export function ScheduleDetailsPage(props: Props) {
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
<ScheduleSummary
|
<ScheduleSummary
|
||||||
handleToggleEnabled={handleToggleEnabled}
|
handleToggleEnabled={handleToggleEnabled}
|
||||||
handleRunBackupNow={() => runBackupNow.mutate({ path: { scheduleId: schedule.id.toString() } })}
|
handleRunBackupNow={() => runBackupNow.mutate({ path: { shortId: schedule.shortId } })}
|
||||||
handleStopBackup={() => stopBackup.mutate({ path: { scheduleId: schedule.id.toString() } })}
|
handleStopBackup={() => stopBackup.mutate({ path: { shortId: schedule.shortId } })}
|
||||||
handleDeleteSchedule={() => deleteSchedule.mutate({ path: { scheduleId: schedule.id.toString() } })}
|
handleDeleteSchedule={() => deleteSchedule.mutate({ path: { shortId: schedule.shortId } })}
|
||||||
setIsEditMode={setIsEditMode}
|
setIsEditMode={setIsEditMode}
|
||||||
schedule={schedule}
|
schedule={schedule}
|
||||||
/>
|
/>
|
||||||
<div className={cn({ hidden: !loaderData.notifs?.length })}>
|
<div className={cn({ hidden: !loaderData.notifs?.length })}>
|
||||||
<ScheduleNotificationsConfig
|
<ScheduleNotificationsConfig
|
||||||
scheduleId={schedule.id}
|
scheduleShortId={schedule.shortId}
|
||||||
destinations={loaderData.notifs ?? []}
|
destinations={loaderData.notifs ?? []}
|
||||||
initialData={loaderData.scheduleNotifs ?? []}
|
initialData={loaderData.scheduleNotifs ?? []}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={cn({ hidden: !loaderData.repos?.length || loaderData.repos.length < 2 })}>
|
<div className={cn({ hidden: !loaderData.repos?.length || loaderData.repos.length < 2 })}>
|
||||||
<ScheduleMirrorsConfig
|
<ScheduleMirrorsConfig
|
||||||
scheduleId={schedule.id}
|
scheduleShortId={schedule.shortId}
|
||||||
primaryRepositoryId={schedule.repositoryId}
|
primaryRepositoryId={schedule.repository.shortId}
|
||||||
repositories={loaderData.repos ?? []}
|
repositories={loaderData.repos ?? []}
|
||||||
initialData={loaderData.mirrors ?? []}
|
initialData={loaderData.mirrors ?? []}
|
||||||
/>
|
/>
|
||||||
|
|
@ -285,7 +288,7 @@ export function ScheduleDetailsPage(props: Props) {
|
||||||
key={selectedSnapshot?.short_id}
|
key={selectedSnapshot?.short_id}
|
||||||
snapshot={selectedSnapshot}
|
snapshot={selectedSnapshot}
|
||||||
repositoryId={schedule.repository.shortId}
|
repositoryId={schedule.repository.shortId}
|
||||||
backupId={schedule.id.toString()}
|
backupId={schedule.shortId}
|
||||||
onDeleteSnapshot={handleDeleteSnapshot}
|
onDeleteSnapshot={handleDeleteSnapshot}
|
||||||
isDeletingSnapshot={deleteSnapshot.isPending}
|
isDeletingSnapshot={deleteSnapshot.isPending}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ export function BackupsPage() {
|
||||||
...listBackupSchedulesOptions(),
|
...listBackupSchedulesOptions(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const [localItems, setLocalItems] = useState<number[] | null>(null);
|
const [localItems, setLocalItems] = useState<string[] | null>(null);
|
||||||
const items = localItems ?? schedules?.map((s) => s.id) ?? [];
|
const items = localItems ?? schedules?.map((s) => s.shortId) ?? [];
|
||||||
|
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(PointerSensor, {
|
useSensor(PointerSensor, {
|
||||||
|
|
@ -48,14 +48,14 @@ export function BackupsPage() {
|
||||||
|
|
||||||
if (over && active.id !== over.id) {
|
if (over && active.id !== over.id) {
|
||||||
setLocalItems((currentItems) => {
|
setLocalItems((currentItems) => {
|
||||||
const baseItems = currentItems ?? schedules?.map((s) => s.id) ?? [];
|
const baseItems = currentItems ?? schedules?.map((s) => s.shortId) ?? [];
|
||||||
const activeId = active.id as number;
|
const activeId = String(active.id);
|
||||||
const overId = over.id as number;
|
const overId = String(over.id);
|
||||||
let oldIndex = baseItems.indexOf(activeId);
|
let oldIndex = baseItems.indexOf(activeId);
|
||||||
let newIndex = baseItems.indexOf(overId);
|
let newIndex = baseItems.indexOf(overId);
|
||||||
|
|
||||||
if (oldIndex === -1 || newIndex === -1) {
|
if (oldIndex === -1 || newIndex === -1) {
|
||||||
const freshItems = schedules?.map((s) => s.id) ?? [];
|
const freshItems = schedules?.map((s) => s.shortId) ?? [];
|
||||||
oldIndex = freshItems.indexOf(activeId);
|
oldIndex = freshItems.indexOf(activeId);
|
||||||
newIndex = freshItems.indexOf(overId);
|
newIndex = freshItems.indexOf(overId);
|
||||||
|
|
||||||
|
|
@ -64,12 +64,12 @@ export function BackupsPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const newItems = arrayMove(freshItems, oldIndex, newIndex);
|
const newItems = arrayMove(freshItems, oldIndex, newIndex);
|
||||||
reorderMutation.mutate({ body: { scheduleIds: newItems } });
|
reorderMutation.mutate({ body: { scheduleShortIds: newItems } });
|
||||||
return newItems;
|
return newItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newItems = arrayMove(baseItems, oldIndex, newIndex);
|
const newItems = arrayMove(baseItems, oldIndex, newIndex);
|
||||||
reorderMutation.mutate({ body: { scheduleIds: newItems } });
|
reorderMutation.mutate({ body: { scheduleShortIds: newItems } });
|
||||||
|
|
||||||
return newItems;
|
return newItems;
|
||||||
});
|
});
|
||||||
|
|
@ -94,7 +94,7 @@ export function BackupsPage() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const scheduleMap = new Map(schedules.map((s) => [s.id, s]));
|
const scheduleMap = new Map(schedules.map((s) => [s.shortId, s]));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container @container mx-auto space-y-6">
|
<div className="container @container mx-auto space-y-6">
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import { Link, useNavigate } from "@tanstack/react-router";
|
||||||
export function CreateBackupPage() {
|
export function CreateBackupPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const formId = useId();
|
const formId = useId();
|
||||||
const [selectedVolumeId, setSelectedVolumeId] = useState<number | undefined>();
|
const [selectedVolumeShortId, setSelectedVolumeShortId] = useState<string | undefined>();
|
||||||
|
|
||||||
const { data: volumesData } = useSuspenseQuery({
|
const { data: volumesData } = useSuspenseQuery({
|
||||||
...listVolumesOptions(),
|
...listVolumesOptions(),
|
||||||
|
|
@ -33,7 +33,7 @@ export function CreateBackupPage() {
|
||||||
...createBackupScheduleMutation(),
|
...createBackupScheduleMutation(),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
toast.success("Backup job created successfully");
|
toast.success("Backup job created successfully");
|
||||||
void navigate({ to: `/backups/${data.id}` });
|
void navigate({ to: `/backups/${data.shortId}` });
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
toast.error("Failed to create backup job", {
|
toast.error("Failed to create backup job", {
|
||||||
|
|
@ -43,7 +43,7 @@ export function CreateBackupPage() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = (formValues: BackupScheduleFormValues) => {
|
const handleSubmit = (formValues: BackupScheduleFormValues) => {
|
||||||
if (!selectedVolumeId) return;
|
if (!selectedVolumeShortId) return;
|
||||||
|
|
||||||
const cronExpression = getCronExpression(
|
const cronExpression = getCronExpression(
|
||||||
formValues.frequency,
|
formValues.frequency,
|
||||||
|
|
@ -64,7 +64,7 @@ export function CreateBackupPage() {
|
||||||
createSchedule.mutate({
|
createSchedule.mutate({
|
||||||
body: {
|
body: {
|
||||||
name: formValues.name,
|
name: formValues.name,
|
||||||
volumeId: selectedVolumeId,
|
volumeId: selectedVolumeShortId,
|
||||||
repositoryId: formValues.repositoryId,
|
repositoryId: formValues.repositoryId,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
cronExpression,
|
cronExpression,
|
||||||
|
|
@ -77,7 +77,7 @@ export function CreateBackupPage() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectedVolume = volumesData.find((v) => v.id === selectedVolumeId);
|
const selectedVolume = volumesData.find((v) => v.shortId === selectedVolumeShortId);
|
||||||
|
|
||||||
if (!volumesData.length) {
|
if (!volumesData.length) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -113,13 +113,13 @@ export function CreateBackupPage() {
|
||||||
<div className="container mx-auto space-y-4">
|
<div className="container mx-auto space-y-4">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Select value={selectedVolumeId?.toString()} onValueChange={(v) => setSelectedVolumeId(Number(v))}>
|
<Select value={selectedVolumeShortId} onValueChange={setSelectedVolumeShortId}>
|
||||||
<SelectTrigger id="volume-select">
|
<SelectTrigger id="volume-select">
|
||||||
<SelectValue placeholder="Choose a volume to backup" />
|
<SelectValue placeholder="Choose a volume to backup" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{volumesData.map((volume) => (
|
{volumesData.map((volume) => (
|
||||||
<SelectItem key={volume.id} value={volume.id.toString()}>
|
<SelectItem key={volume.shortId} value={volume.shortId}>
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<HardDrive className="h-4 w-4" />
|
<HardDrive className="h-4 w-4" />
|
||||||
{volume.name}
|
{volume.name}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ export function EditRepositoryPage({ repositoryId }: { repositoryId: string }) {
|
||||||
const [pendingValues, setPendingValues] = useState<RepositoryFormValues | null>(null);
|
const [pendingValues, setPendingValues] = useState<RepositoryFormValues | null>(null);
|
||||||
|
|
||||||
const { data: repository } = useSuspenseQuery({
|
const { data: repository } = useSuspenseQuery({
|
||||||
...getRepositoryOptions({ path: { id: repositoryId } }),
|
...getRepositoryOptions({ path: { shortId: repositoryId } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateRepository = useMutation({
|
const updateRepository = useMutation({
|
||||||
|
|
@ -76,7 +76,7 @@ export function EditRepositoryPage({ repositoryId }: { repositoryId: string }) {
|
||||||
|
|
||||||
const submitUpdate = (values: RepositoryFormValues) => {
|
const submitUpdate = (values: RepositoryFormValues) => {
|
||||||
updateRepository.mutate({
|
updateRepository.mutate({
|
||||||
path: { id: repositoryId },
|
path: { shortId: repositoryId },
|
||||||
body: {
|
body: {
|
||||||
name: values.name,
|
name: values.name,
|
||||||
compressionMode: values.compressionMode,
|
compressionMode: values.compressionMode,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export default function RepositoryDetailsPage({ repositoryId }: { repositoryId:
|
||||||
const activeTab = tab || "info";
|
const activeTab = tab || "info";
|
||||||
|
|
||||||
const { data } = useSuspenseQuery({
|
const { data } = useSuspenseQuery({
|
||||||
...getRepositoryOptions({ path: { id: repositoryId } }),
|
...getRepositoryOptions({ path: { shortId: repositoryId } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ export function SnapshotDetailsPage({ repositoryId, snapshotId }: { repositoryId
|
||||||
const [showAllPaths, setShowAllPaths] = useState(false);
|
const [showAllPaths, setShowAllPaths] = useState(false);
|
||||||
|
|
||||||
const { data: repository } = useSuspenseQuery({
|
const { data: repository } = useSuspenseQuery({
|
||||||
...getRepositoryOptions({ path: { id: repositoryId } }),
|
...getRepositoryOptions({ path: { shortId: repositoryId } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: schedules } = useSuspenseQuery({
|
const { data: schedules } = useSuspenseQuery({
|
||||||
|
|
@ -61,7 +61,7 @@ export function SnapshotDetailsPage({ repositoryId, snapshotId }: { repositoryId
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data, error } = useQuery({
|
const { data, error } = useQuery({
|
||||||
...getSnapshotDetailsOptions({ path: { id: repositoryId, snapshotId: snapshotId } }),
|
...getSnapshotDetailsOptions({ path: { shortId: repositoryId, snapshotId: snapshotId } }),
|
||||||
});
|
});
|
||||||
const backupSchedule = schedules?.find((s) => data?.tags?.includes(s.shortId));
|
const backupSchedule = schedules?.find((s) => data?.tags?.includes(s.shortId));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ export const RepositoryInfoTabContent = ({ repository }: Props) => {
|
||||||
|
|
||||||
const handleConfirmDelete = () => {
|
const handleConfirmDelete = () => {
|
||||||
setShowDeleteConfirm(false);
|
setShowDeleteConfirm(false);
|
||||||
deleteRepo.mutate({ path: { id: repository.id } });
|
deleteRepo.mutate({ path: { shortId: repository.shortId } });
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = repository.config as RepositoryConfig;
|
const config = repository.config as RepositoryConfig;
|
||||||
|
|
@ -115,7 +115,7 @@ export const RepositoryInfoTabContent = ({ repository }: Props) => {
|
||||||
type="button"
|
type="button"
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
loading={cancelDoctor.isPending}
|
loading={cancelDoctor.isPending}
|
||||||
onClick={() => cancelDoctor.mutate({ path: { id: repository.id } })}
|
onClick={() => cancelDoctor.mutate({ path: { shortId: repository.shortId } })}
|
||||||
>
|
>
|
||||||
<Square className="h-4 w-4 mr-2" />
|
<Square className="h-4 w-4 mr-2" />
|
||||||
<span>Cancel doctor</span>
|
<span>Cancel doctor</span>
|
||||||
|
|
@ -123,7 +123,7 @@ export const RepositoryInfoTabContent = ({ repository }: Props) => {
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => startDoctor.mutate({ path: { id: repository.id } })}
|
onClick={() => startDoctor.mutate({ path: { shortId: repository.shortId } })}
|
||||||
disabled={startDoctor.isPending}
|
disabled={startDoctor.isPending}
|
||||||
>
|
>
|
||||||
<Stethoscope className="h-4 w-4 mr-2" />
|
<Stethoscope className="h-4 w-4 mr-2" />
|
||||||
|
|
@ -133,7 +133,7 @@ export const RepositoryInfoTabContent = ({ repository }: Props) => {
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => unlockRepo.mutate({ path: { id: repository.id } })}
|
onClick={() => unlockRepo.mutate({ path: { shortId: repository.shortId } })}
|
||||||
loading={unlockRepo.isPending}
|
loading={unlockRepo.isPending}
|
||||||
>
|
>
|
||||||
<Unlock className="h-4 w-4 mr-2" />
|
<Unlock className="h-4 w-4 mr-2" />
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export const RepositorySnapshotsTabContent = ({ repository }: Props) => {
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
|
||||||
const { data, isFetching, failureReason } = useQuery({
|
const { data, isFetching, failureReason } = useQuery({
|
||||||
...listSnapshotsOptions({ path: { id: repository.id } }),
|
...listSnapshotsOptions({ path: { shortId: repository.shortId } }),
|
||||||
initialData: [],
|
initialData: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -41,7 +41,7 @@ export const RepositorySnapshotsTabContent = ({ repository }: Props) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
refreshMutation.mutate({ path: { id: repository.id } });
|
refreshMutation.mutate({ path: { shortId: repository.shortId } });
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredSnapshots = data.filter((snapshot: Snapshot) => {
|
const filteredSnapshots = data.filter((snapshot: Snapshot) => {
|
||||||
|
|
@ -173,7 +173,7 @@ export const RepositorySnapshotsTabContent = ({ repository }: Props) => {
|
||||||
snapshots={filteredSnapshots}
|
snapshots={filteredSnapshots}
|
||||||
repositoryId={repository.shortId}
|
repositoryId={repository.shortId}
|
||||||
backups={schedules.data ?? []}
|
backups={schedules.data ?? []}
|
||||||
listSnapshotsQueryOptions={{ path: { id: repository.id } }}
|
listSnapshotsQueryOptions={{ path: { shortId: repository.shortId } }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="px-4 py-2 text-sm text-muted-foreground bg-card-header flex justify-between border-t">
|
<div className="px-4 py-2 text-sm text-muted-foreground bg-card-header flex justify-between border-t">
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,10 @@ export const HealthchecksCard = ({ volume }: Props) => {
|
||||||
<OnOff
|
<OnOff
|
||||||
isOn={volume.autoRemount}
|
isOn={volume.autoRemount}
|
||||||
toggle={() =>
|
toggle={() =>
|
||||||
toggleAutoRemount.mutate({ path: { id: volume.shortId }, body: { autoRemount: !volume.autoRemount } })
|
toggleAutoRemount.mutate({
|
||||||
|
path: { shortId: volume.shortId },
|
||||||
|
body: { autoRemount: !volume.autoRemount },
|
||||||
|
})
|
||||||
}
|
}
|
||||||
disabled={toggleAutoRemount.isPending}
|
disabled={toggleAutoRemount.isPending}
|
||||||
enabledLabel="Enabled"
|
enabledLabel="Enabled"
|
||||||
|
|
@ -74,7 +77,7 @@ export const HealthchecksCard = ({ volume }: Props) => {
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="mt-4"
|
className="mt-4"
|
||||||
loading={healthcheck.isPending}
|
loading={healthcheck.isPending}
|
||||||
onClick={() => healthcheck.mutate({ path: { id: volume.shortId } })}
|
onClick={() => healthcheck.mutate({ path: { shortId: volume.shortId } })}
|
||||||
>
|
>
|
||||||
<Activity className="h-4 w-4 mr-2" />
|
<Activity className="h-4 w-4 mr-2" />
|
||||||
Run Health Check
|
Run Health Check
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export function VolumeDetails({ volumeId }: { volumeId: string }) {
|
||||||
const activeTab = searchParams.tab || "info";
|
const activeTab = searchParams.tab || "info";
|
||||||
|
|
||||||
const { data } = useSuspenseQuery({
|
const { data } = useSuspenseQuery({
|
||||||
...getVolumeOptions({ path: { id: volumeId } }),
|
...getVolumeOptions({ path: { shortId: volumeId } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { volume, statfs } = data;
|
const { volume, statfs } = data;
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => {
|
||||||
const confirmUpdate = () => {
|
const confirmUpdate = () => {
|
||||||
if (pendingValues) {
|
if (pendingValues) {
|
||||||
updateMutation.mutate({
|
updateMutation.mutate({
|
||||||
path: { id: volume.shortId },
|
path: { shortId: volume.shortId },
|
||||||
body: { name: pendingValues.name, config: pendingValues },
|
body: { name: pendingValues.name, config: pendingValues },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +111,7 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => {
|
||||||
|
|
||||||
const handleConfirmDelete = () => {
|
const handleConfirmDelete = () => {
|
||||||
setShowDeleteConfirm(false);
|
setShowDeleteConfirm(false);
|
||||||
deleteVol.mutate({ path: { id: volume.shortId } });
|
deleteVol.mutate({ path: { shortId: volume.shortId } });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -126,7 +126,7 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => {
|
||||||
{volume.status !== "mounted" ? (
|
{volume.status !== "mounted" ? (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => mountVol.mutate({ path: { id: volume.shortId } })}
|
onClick={() => mountVol.mutate({ path: { shortId: volume.shortId } })}
|
||||||
loading={mountVol.isPending}
|
loading={mountVol.isPending}
|
||||||
>
|
>
|
||||||
<Plug className="h-4 w-4 mr-2" />
|
<Plug className="h-4 w-4 mr-2" />
|
||||||
|
|
@ -136,7 +136,7 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => {
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => unmountVol.mutate({ path: { id: volume.shortId } })}
|
onClick={() => unmountVol.mutate({ path: { shortId: volume.shortId } })}
|
||||||
loading={unmountVol.isPending}
|
loading={unmountVol.isPending}
|
||||||
>
|
>
|
||||||
<Unplug className="h-4 w-4 mr-2" />
|
<Unplug className="h-4 w-4 mr-2" />
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { getVolumeMountPath } from "~/client/lib/volume-path";
|
||||||
export const Route = createFileRoute("/(dashboard)/backups/$backupId/$snapshotId/restore")({
|
export const Route = createFileRoute("/(dashboard)/backups/$backupId/$snapshotId/restore")({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
loader: async ({ params, context }) => {
|
loader: async ({ params, context }) => {
|
||||||
const schedule = await getBackupSchedule({ path: { scheduleId: params.backupId } });
|
const schedule = await getBackupSchedule({ path: { shortId: params.backupId } });
|
||||||
|
|
||||||
if (!schedule.data) {
|
if (!schedule.data) {
|
||||||
throw new Response("Not Found", { status: 404 });
|
throw new Response("Not Found", { status: 404 });
|
||||||
|
|
@ -15,9 +15,13 @@ export const Route = createFileRoute("/(dashboard)/backups/$backupId/$snapshotId
|
||||||
|
|
||||||
const [snapshot, repository] = await Promise.all([
|
const [snapshot, repository] = await Promise.all([
|
||||||
context.queryClient.ensureQueryData({
|
context.queryClient.ensureQueryData({
|
||||||
...getSnapshotDetailsOptions({ path: { id: schedule.data?.repositoryId, snapshotId: params.snapshotId } }),
|
...getSnapshotDetailsOptions({
|
||||||
|
path: { shortId: schedule.data.repository.shortId, snapshotId: params.snapshotId },
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
context.queryClient.ensureQueryData({
|
||||||
|
...getRepositoryOptions({ path: { shortId: schedule.data.repository.shortId } }),
|
||||||
}),
|
}),
|
||||||
context.queryClient.ensureQueryData({ ...getRepositoryOptions({ path: { id: schedule.data?.repositoryId } }) }),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,19 @@ export const Route = createFileRoute("/(dashboard)/backups/$backupId/")({
|
||||||
const { backupId } = params;
|
const { backupId } = params;
|
||||||
|
|
||||||
const [schedule, notifs, repos, scheduleNotifs, mirrors] = await Promise.all([
|
const [schedule, notifs, repos, scheduleNotifs, mirrors] = await Promise.all([
|
||||||
context.queryClient.ensureQueryData({ ...getBackupScheduleOptions({ path: { scheduleId: backupId } }) }),
|
context.queryClient.ensureQueryData({ ...getBackupScheduleOptions({ path: { shortId: backupId } }) }),
|
||||||
context.queryClient.ensureQueryData({ ...listNotificationDestinationsOptions() }),
|
context.queryClient.ensureQueryData({ ...listNotificationDestinationsOptions() }),
|
||||||
context.queryClient.ensureQueryData({ ...listRepositoriesOptions() }),
|
context.queryClient.ensureQueryData({ ...listRepositoriesOptions() }),
|
||||||
context.queryClient.ensureQueryData({ ...getScheduleNotificationsOptions({ path: { scheduleId: backupId } }) }),
|
context.queryClient.ensureQueryData({ ...getScheduleNotificationsOptions({ path: { shortId: backupId } }) }),
|
||||||
context.queryClient.ensureQueryData({ ...getScheduleMirrorsOptions({ path: { scheduleId: backupId } }) }),
|
context.queryClient.ensureQueryData({ ...getScheduleMirrorsOptions({ path: { shortId: backupId } }) }),
|
||||||
])
|
]);
|
||||||
|
|
||||||
void context.queryClient.prefetchQuery({
|
void context.queryClient.prefetchQuery({
|
||||||
...listSnapshotsOptions({ path: { id: schedule.repository.id }, query: { backupId: schedule.shortId } }),
|
...listSnapshotsOptions({
|
||||||
})
|
path: { shortId: schedule.repository.shortId },
|
||||||
|
query: { backupId: schedule.shortId },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
return { schedule, notifs, repos, scheduleNotifs, mirrors };
|
return { schedule, notifs, repos, scheduleNotifs, mirrors };
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ export const Route = createFileRoute("/(dashboard)/repositories/$repositoryId/$s
|
||||||
errorComponent: (e) => <div>{e.error.message}</div>,
|
errorComponent: (e) => <div>{e.error.message}</div>,
|
||||||
loader: async ({ params, context }) => {
|
loader: async ({ params, context }) => {
|
||||||
const res = await context.queryClient.ensureQueryData({
|
const res = await context.queryClient.ensureQueryData({
|
||||||
...getRepositoryOptions({ path: { id: params.repositoryId } }),
|
...getRepositoryOptions({ path: { shortId: params.repositoryId } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,15 @@ export const Route = createFileRoute("/(dashboard)/repositories/$repositoryId/$s
|
||||||
loader: async ({ params, context }) => {
|
loader: async ({ params, context }) => {
|
||||||
const [snapshot, repository] = await Promise.all([
|
const [snapshot, repository] = await Promise.all([
|
||||||
context.queryClient.ensureQueryData({
|
context.queryClient.ensureQueryData({
|
||||||
...getSnapshotDetailsOptions({ path: { id: params.repositoryId, snapshotId: params.snapshotId } }),
|
...getSnapshotDetailsOptions({ path: { shortId: params.repositoryId, snapshotId: params.snapshotId } }),
|
||||||
}),
|
}),
|
||||||
context.queryClient.ensureQueryData({ ...getRepositoryOptions({ path: { id: params.repositoryId } }) }),
|
context.queryClient.ensureQueryData({ ...getRepositoryOptions({ path: { shortId: params.repositoryId } }) }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let basePath: string | undefined;
|
let basePath: string | undefined;
|
||||||
const scheduleShortId = snapshot.tags?.[0];
|
const scheduleShortId = snapshot.tags?.[0];
|
||||||
if (scheduleShortId) {
|
if (scheduleShortId) {
|
||||||
const scheduleRes = await getBackupSchedule({ path: { scheduleId: scheduleShortId } });
|
const scheduleRes = await getBackupSchedule({ path: { shortId: scheduleShortId } });
|
||||||
if (scheduleRes.data) {
|
if (scheduleRes.data) {
|
||||||
basePath = getVolumeMountPath(scheduleRes.data.volume);
|
basePath = getVolumeMountPath(scheduleRes.data.volume);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ export const Route = createFileRoute("/(dashboard)/repositories/$repositoryId/ed
|
||||||
errorComponent: (e) => <div>{e.error.message}</div>,
|
errorComponent: (e) => <div>{e.error.message}</div>,
|
||||||
loader: async ({ params, context }) => {
|
loader: async ({ params, context }) => {
|
||||||
const repository = await context.queryClient.ensureQueryData({
|
const repository = await context.queryClient.ensureQueryData({
|
||||||
...getRepositoryOptions({ path: { id: params.repositoryId } }),
|
...getRepositoryOptions({ path: { shortId: params.repositoryId } }),
|
||||||
})
|
});
|
||||||
|
|
||||||
return repository;
|
return repository;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,15 @@ export const Route = createFileRoute("/(dashboard)/repositories/$repositoryId/")
|
||||||
errorComponent: (e) => <div>{e.error.message}</div>,
|
errorComponent: (e) => <div>{e.error.message}</div>,
|
||||||
loader: async ({ params, context }) => {
|
loader: async ({ params, context }) => {
|
||||||
void context.queryClient.prefetchQuery({
|
void context.queryClient.prefetchQuery({
|
||||||
...listSnapshotsOptions({ path: { id: params.repositoryId } }),
|
...listSnapshotsOptions({ path: { shortId: params.repositoryId } }),
|
||||||
})
|
});
|
||||||
void context.queryClient.prefetchQuery({
|
void context.queryClient.prefetchQuery({
|
||||||
...listBackupSchedulesOptions(),
|
...listBackupSchedulesOptions(),
|
||||||
})
|
});
|
||||||
|
|
||||||
const res = await context.queryClient.ensureQueryData({
|
const res = await context.queryClient.ensureQueryData({
|
||||||
...getRepositoryOptions({ path: { id: params.repositoryId } }),
|
...getRepositoryOptions({ path: { shortId: params.repositoryId } }),
|
||||||
})
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export const Route = createFileRoute("/(dashboard)/volumes/$volumeId")({
|
||||||
errorComponent: (e) => <div>{e.error.message}</div>,
|
errorComponent: (e) => <div>{e.error.message}</div>,
|
||||||
loader: async ({ params, context }) => {
|
loader: async ({ params, context }) => {
|
||||||
const res = await context.queryClient.ensureQueryData({
|
const res = await context.queryClient.ensureQueryData({
|
||||||
...getVolumeOptions({ path: { id: params.volumeId } }),
|
...getVolumeOptions({ path: { shortId: params.volumeId } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export const backupEventStatusSchema = type("'success' | 'error' | 'stopped' | '
|
||||||
export const restoreEventStatusSchema = type("'success' | 'error'");
|
export const restoreEventStatusSchema = type("'success' | 'error'");
|
||||||
|
|
||||||
const backupEventBaseSchema = type({
|
const backupEventBaseSchema = type({
|
||||||
scheduleId: "number",
|
scheduleId: "string",
|
||||||
volumeName: "string",
|
volumeName: "string",
|
||||||
repositoryName: "string",
|
repositoryName: "string",
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,13 @@ export const serverEventPayloads = {
|
||||||
"restore:completed": payload<ServerRestoreCompletedEventDto>(),
|
"restore:completed": payload<ServerRestoreCompletedEventDto>(),
|
||||||
"mirror:started": payload<{
|
"mirror:started": payload<{
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
scheduleId: number;
|
scheduleId: string;
|
||||||
repositoryId: string;
|
repositoryId: string;
|
||||||
repositoryName: string;
|
repositoryName: string;
|
||||||
}>(),
|
}>(),
|
||||||
"mirror:completed": payload<{
|
"mirror:completed": payload<{
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
scheduleId: number;
|
scheduleId: string;
|
||||||
repositoryId: string;
|
repositoryId: string;
|
||||||
repositoryName: string;
|
repositoryName: string;
|
||||||
status: "success" | "error";
|
status: "success" | "error";
|
||||||
|
|
|
||||||
|
|
@ -61,17 +61,17 @@ describe("multi-organization isolation", () => {
|
||||||
expect(session1.organizationId).not.toBe(session2.organizationId);
|
expect(session1.organizationId).not.toBe(session2.organizationId);
|
||||||
|
|
||||||
const repoId = crypto.randomUUID();
|
const repoId = crypto.randomUUID();
|
||||||
const shortId = generateShortId();
|
const repoShortId = generateShortId();
|
||||||
await db.insert(repositoriesTable).values({
|
await db.insert(repositoriesTable).values({
|
||||||
id: repoId,
|
id: repoId,
|
||||||
shortId,
|
shortId: repoShortId,
|
||||||
name: "Org 1 Repo",
|
name: "Org 1 Repo",
|
||||||
type: "local",
|
type: "local",
|
||||||
config: { backend: "local", name: "org1repo", path: "/tmp/repo1" },
|
config: { backend: "local", name: "org1repo", path: "/tmp/repo1" },
|
||||||
organizationId: session1.organizationId,
|
organizationId: session1.organizationId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await app.request(`/api/v1/repositories/${repoId}`, {
|
const res = await app.request(`/api/v1/repositories/${repoShortId}`, {
|
||||||
headers: getAuthHeaders(session2.token),
|
headers: getAuthHeaders(session2.token),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -79,7 +79,7 @@ describe("multi-organization isolation", () => {
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
expect(body.message).toBe("Repository not found");
|
expect(body.message).toBe("Repository not found");
|
||||||
|
|
||||||
const resOk = await app.request(`/api/v1/repositories/${repoId}`, {
|
const resOk = await app.request(`/api/v1/repositories/${repoShortId}`, {
|
||||||
headers: getAuthHeaders(session1.token),
|
headers: getAuthHeaders(session1.token),
|
||||||
});
|
});
|
||||||
expect(resOk.status).toBe(200);
|
expect(resOk.status).toBe(200);
|
||||||
|
|
@ -128,9 +128,10 @@ describe("multi-organization isolation", () => {
|
||||||
const session2 = await createTestSession();
|
const session2 = await createTestSession();
|
||||||
|
|
||||||
const volumeId = Math.floor(Math.random() * 1000000);
|
const volumeId = Math.floor(Math.random() * 1000000);
|
||||||
|
const volumeShortId = generateShortId();
|
||||||
await db.insert(volumesTable).values({
|
await db.insert(volumesTable).values({
|
||||||
id: volumeId,
|
id: volumeId,
|
||||||
shortId: generateShortId(),
|
shortId: volumeShortId,
|
||||||
name: "Org 1 Volume",
|
name: "Org 1 Volume",
|
||||||
type: "directory",
|
type: "directory",
|
||||||
config: { backend: "directory", path: "/tmp/vol1" },
|
config: { backend: "directory", path: "/tmp/vol1" },
|
||||||
|
|
@ -138,7 +139,7 @@ describe("multi-organization isolation", () => {
|
||||||
status: "unmounted",
|
status: "unmounted",
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await app.request(`/api/v1/volumes/${volumeId}`, {
|
const res = await app.request(`/api/v1/volumes/${volumeShortId}`, {
|
||||||
headers: getAuthHeaders(session2.token),
|
headers: getAuthHeaders(session2.token),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -224,13 +225,13 @@ describe("multi-organization isolation", () => {
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
const res = await app.request(`/api/v1/backups/${schedule.id}`, {
|
const res = await app.request(`/api/v1/backups/${schedule.shortId}`, {
|
||||||
headers: getAuthHeaders(session2.token),
|
headers: getAuthHeaders(session2.token),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(res.status).toBe(404);
|
expect(res.status).toBe(404);
|
||||||
|
|
||||||
const resOk = await app.request(`/api/v1/backups/${schedule.id}`, {
|
const resOk = await app.request(`/api/v1/backups/${schedule.shortId}`, {
|
||||||
headers: getAuthHeaders(session1.token),
|
headers: getAuthHeaders(session1.token),
|
||||||
});
|
});
|
||||||
expect(resOk.status).toBe(200);
|
expect(resOk.status).toBe(200);
|
||||||
|
|
@ -293,12 +294,12 @@ describe("multi-organization isolation", () => {
|
||||||
notifyOnFailure: true,
|
notifyOnFailure: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const resGet = await app.request(`/api/v1/backups/${schedule.id}/notifications`, {
|
const resGet = await app.request(`/api/v1/backups/${schedule.shortId}/notifications`, {
|
||||||
headers: getAuthHeaders(session2.token),
|
headers: getAuthHeaders(session2.token),
|
||||||
});
|
});
|
||||||
expect(resGet.status).toBe(404);
|
expect(resGet.status).toBe(404);
|
||||||
|
|
||||||
const resPut = await app.request(`/api/v1/backups/${schedule.id}/notifications`, {
|
const resPut = await app.request(`/api/v1/backups/${schedule.shortId}/notifications`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
...getAuthHeaders(session2.token),
|
...getAuthHeaders(session2.token),
|
||||||
|
|
|
||||||
|
|
@ -222,10 +222,8 @@ describe("getScheduleByIdOrShortId", () => {
|
||||||
organizationId: otherOrgId,
|
organizationId: otherOrgId,
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(backupsService.getScheduleByIdOrShortId(schedule.shortId)).rejects.toThrow(
|
expect(backupsService.getScheduleByIdOrShortId(schedule.shortId)).rejects.toThrow("Backup schedule not found");
|
||||||
"Backup schedule not found",
|
expect(backupsService.getScheduleByIdOrShortId(schedule.id)).rejects.toThrow("Backup schedule not found");
|
||||||
);
|
|
||||||
await expect(backupsService.getScheduleByIdOrShortId(schedule.id)).rejects.toThrow("Backup schedule not found");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,15 +52,15 @@ export const backupScheduleController = new Hono()
|
||||||
|
|
||||||
return c.json<ListBackupSchedulesResponseDto>(schedules, 200);
|
return c.json<ListBackupSchedulesResponseDto>(schedules, 200);
|
||||||
})
|
})
|
||||||
.get("/:scheduleId", getBackupScheduleDto, async (c) => {
|
.get("/:shortId", getBackupScheduleDto, async (c) => {
|
||||||
const scheduleId = c.req.param("scheduleId");
|
const shortId = c.req.param("shortId");
|
||||||
const schedule = await backupsService.getScheduleByIdOrShortId(scheduleId);
|
const schedule = await backupsService.getScheduleByShortId(shortId);
|
||||||
|
|
||||||
return c.json<GetBackupScheduleDto>(schedule, 200);
|
return c.json<GetBackupScheduleDto>(schedule, 200);
|
||||||
})
|
})
|
||||||
.get("/volume/:volumeId", getBackupScheduleForVolumeDto, async (c) => {
|
.get("/volume/:volumeShortId", getBackupScheduleForVolumeDto, async (c) => {
|
||||||
const volumeId = c.req.param("volumeId");
|
const volumeShortId = c.req.param("volumeShortId");
|
||||||
const schedule = await backupsService.getScheduleForVolume(Number(volumeId));
|
const schedule = await backupsService.getScheduleForVolume(volumeShortId);
|
||||||
|
|
||||||
return c.json<GetBackupScheduleForVolumeResponseDto>(schedule, 200);
|
return c.json<GetBackupScheduleForVolumeResponseDto>(schedule, 200);
|
||||||
})
|
})
|
||||||
|
|
@ -70,22 +70,23 @@ export const backupScheduleController = new Hono()
|
||||||
|
|
||||||
return c.json<CreateBackupScheduleDto>(schedule, 201);
|
return c.json<CreateBackupScheduleDto>(schedule, 201);
|
||||||
})
|
})
|
||||||
.patch("/:scheduleId", updateBackupScheduleDto, validator("json", updateBackupScheduleBody), async (c) => {
|
.patch("/:shortId", updateBackupScheduleDto, validator("json", updateBackupScheduleBody), async (c) => {
|
||||||
const scheduleId = c.req.param("scheduleId");
|
const shortId = c.req.param("shortId");
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json");
|
||||||
const schedule = await backupsService.updateSchedule(Number(scheduleId), body);
|
const schedule = await backupsService.updateSchedule(shortId, body);
|
||||||
|
|
||||||
return c.json<UpdateBackupScheduleDto>(schedule, 200);
|
return c.json<UpdateBackupScheduleDto>(schedule, 200);
|
||||||
})
|
})
|
||||||
.delete("/:scheduleId", deleteBackupScheduleDto, async (c) => {
|
.delete("/:shortId", deleteBackupScheduleDto, async (c) => {
|
||||||
const scheduleId = c.req.param("scheduleId");
|
const shortId = c.req.param("shortId");
|
||||||
await backupsService.deleteSchedule(Number(scheduleId));
|
await backupsService.deleteSchedule(shortId);
|
||||||
|
|
||||||
return c.json<DeleteBackupScheduleDto>({ success: true }, 200);
|
return c.json<DeleteBackupScheduleDto>({ success: true }, 200);
|
||||||
})
|
})
|
||||||
.post("/:scheduleId/run", runBackupNowDto, async (c) => {
|
.post("/:shortId/run", runBackupNowDto, async (c) => {
|
||||||
const scheduleId = c.req.param("scheduleId");
|
const shortId = c.req.param("shortId");
|
||||||
const result = await backupsExecutionService.validateBackupExecution(Number(scheduleId), true);
|
const schedule = await backupsService.getScheduleByShortId(shortId);
|
||||||
|
const result = await backupsExecutionService.validateBackupExecution(schedule.id, true);
|
||||||
|
|
||||||
if (result.type === "failure") {
|
if (result.type === "failure") {
|
||||||
throw result.error;
|
throw result.error;
|
||||||
|
|
@ -95,64 +96,68 @@ export const backupScheduleController = new Hono()
|
||||||
return c.json<RunBackupNowDto>({ success: true }, 200);
|
return c.json<RunBackupNowDto>({ success: true }, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
backupsExecutionService.executeBackup(Number(scheduleId), true).catch((err) => {
|
backupsExecutionService.executeBackup(schedule.id, true).catch((err) => {
|
||||||
logger.error(`Error executing manual backup for schedule ${scheduleId}:`, err);
|
logger.error(`Error executing manual backup for schedule ${shortId}:`, err);
|
||||||
});
|
});
|
||||||
|
|
||||||
return c.json<RunBackupNowDto>({ success: true }, 200);
|
return c.json<RunBackupNowDto>({ success: true }, 200);
|
||||||
})
|
})
|
||||||
.post("/:scheduleId/stop", stopBackupDto, async (c) => {
|
.post("/:shortId/stop", stopBackupDto, async (c) => {
|
||||||
const scheduleId = c.req.param("scheduleId");
|
const shortId = c.req.param("shortId");
|
||||||
await backupsExecutionService.stopBackup(Number(scheduleId));
|
const schedule = await backupsService.getScheduleByShortId(shortId);
|
||||||
|
await backupsExecutionService.stopBackup(schedule.id);
|
||||||
|
|
||||||
return c.json<StopBackupDto>({ success: true }, 200);
|
return c.json<StopBackupDto>({ success: true }, 200);
|
||||||
})
|
})
|
||||||
.post("/:scheduleId/forget", runForgetDto, async (c) => {
|
.post("/:shortId/forget", runForgetDto, async (c) => {
|
||||||
const scheduleId = c.req.param("scheduleId");
|
const shortId = c.req.param("shortId");
|
||||||
await backupsExecutionService.runForget(Number(scheduleId));
|
const schedule = await backupsService.getScheduleByShortId(shortId);
|
||||||
|
await backupsExecutionService.runForget(schedule.id);
|
||||||
|
|
||||||
return c.json<RunForgetDto>({ success: true }, 200);
|
return c.json<RunForgetDto>({ success: true }, 200);
|
||||||
})
|
})
|
||||||
.get("/:scheduleId/notifications", getScheduleNotificationsDto, async (c) => {
|
.get("/:shortId/notifications", getScheduleNotificationsDto, async (c) => {
|
||||||
const scheduleId = Number.parseInt(c.req.param("scheduleId"), 10);
|
const shortId = c.req.param("shortId");
|
||||||
const assignments = await notificationsService.getScheduleNotifications(scheduleId);
|
const schedule = await backupsService.getScheduleByShortId(shortId);
|
||||||
|
const assignments = await notificationsService.getScheduleNotifications(schedule.id);
|
||||||
|
|
||||||
return c.json<GetScheduleNotificationsDto>(assignments, 200);
|
return c.json<GetScheduleNotificationsDto>(assignments, 200);
|
||||||
})
|
})
|
||||||
.put(
|
.put(
|
||||||
"/:scheduleId/notifications",
|
"/:shortId/notifications",
|
||||||
updateScheduleNotificationsDto,
|
updateScheduleNotificationsDto,
|
||||||
validator("json", updateScheduleNotificationsBody),
|
validator("json", updateScheduleNotificationsBody),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const scheduleId = Number.parseInt(c.req.param("scheduleId"), 10);
|
const shortId = c.req.param("shortId");
|
||||||
|
const schedule = await backupsService.getScheduleByShortId(shortId);
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json");
|
||||||
const assignments = await notificationsService.updateScheduleNotifications(scheduleId, body.assignments);
|
const assignments = await notificationsService.updateScheduleNotifications(schedule.id, body.assignments);
|
||||||
|
|
||||||
return c.json<UpdateScheduleNotificationsDto>(assignments, 200);
|
return c.json<UpdateScheduleNotificationsDto>(assignments, 200);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.get("/:scheduleId/mirrors", getScheduleMirrorsDto, async (c) => {
|
.get("/:shortId/mirrors", getScheduleMirrorsDto, async (c) => {
|
||||||
const scheduleId = Number.parseInt(c.req.param("scheduleId"), 10);
|
const shortId = c.req.param("shortId");
|
||||||
const mirrors = await backupsService.getMirrors(scheduleId);
|
const mirrors = await backupsService.getMirrors(shortId);
|
||||||
|
|
||||||
return c.json<GetScheduleMirrorsDto>(mirrors, 200);
|
return c.json<GetScheduleMirrorsDto>(mirrors, 200);
|
||||||
})
|
})
|
||||||
.put("/:scheduleId/mirrors", updateScheduleMirrorsDto, validator("json", updateScheduleMirrorsBody), async (c) => {
|
.put("/:shortId/mirrors", updateScheduleMirrorsDto, validator("json", updateScheduleMirrorsBody), async (c) => {
|
||||||
const scheduleId = Number.parseInt(c.req.param("scheduleId"), 10);
|
const shortId = c.req.param("shortId");
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json");
|
||||||
const mirrors = await backupsService.updateMirrors(scheduleId, body);
|
const mirrors = await backupsService.updateMirrors(shortId, body);
|
||||||
|
|
||||||
return c.json<UpdateScheduleMirrorsDto>(mirrors, 200);
|
return c.json<UpdateScheduleMirrorsDto>(mirrors, 200);
|
||||||
})
|
})
|
||||||
.get("/:scheduleId/mirrors/compatibility", getMirrorCompatibilityDto, async (c) => {
|
.get("/:shortId/mirrors/compatibility", getMirrorCompatibilityDto, async (c) => {
|
||||||
const scheduleId = Number.parseInt(c.req.param("scheduleId"), 10);
|
const shortId = c.req.param("shortId");
|
||||||
const compatibility = await backupsService.getMirrorCompatibility(scheduleId);
|
const compatibility = await backupsService.getMirrorCompatibility(shortId);
|
||||||
|
|
||||||
return c.json<GetMirrorCompatibilityDto>(compatibility, 200);
|
return c.json<GetMirrorCompatibilityDto>(compatibility, 200);
|
||||||
})
|
})
|
||||||
.post("/reorder", reorderBackupSchedulesDto, validator("json", reorderBackupSchedulesBody), async (c) => {
|
.post("/reorder", reorderBackupSchedulesDto, validator("json", reorderBackupSchedulesBody), async (c) => {
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json");
|
||||||
await backupsService.reorderSchedules(body.scheduleIds);
|
await backupsService.reorderSchedules(body.scheduleShortIds);
|
||||||
|
|
||||||
return c.json<ReorderBackupSchedulesDto>({ success: true }, 200);
|
return c.json<ReorderBackupSchedulesDto>({ success: true }, 200);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ const backupScheduleSchema = type({
|
||||||
);
|
);
|
||||||
|
|
||||||
const scheduleMirrorSchema = type({
|
const scheduleMirrorSchema = type({
|
||||||
scheduleId: "number",
|
scheduleId: "string",
|
||||||
repositoryId: "string",
|
repositoryId: "string",
|
||||||
enabled: "boolean",
|
enabled: "boolean",
|
||||||
lastCopyAt: "number | null",
|
lastCopyAt: "number | null",
|
||||||
|
|
@ -125,7 +125,7 @@ export const getBackupScheduleForVolumeDto = describeRoute({
|
||||||
*/
|
*/
|
||||||
export const createBackupScheduleBody = type({
|
export const createBackupScheduleBody = type({
|
||||||
name: "1 <= string <= 128",
|
name: "1 <= string <= 128",
|
||||||
volumeId: "number",
|
volumeId: "string | number",
|
||||||
repositoryId: "string",
|
repositoryId: "string",
|
||||||
enabled: "boolean",
|
enabled: "boolean",
|
||||||
cronExpression: "string",
|
cronExpression: "string",
|
||||||
|
|
@ -376,7 +376,7 @@ export const getMirrorCompatibilityDto = describeRoute({
|
||||||
* Reorder backup schedules
|
* Reorder backup schedules
|
||||||
*/
|
*/
|
||||||
export const reorderBackupSchedulesBody = type({
|
export const reorderBackupSchedulesBody = type({
|
||||||
scheduleIds: "number[]",
|
scheduleShortIds: "string[]",
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ReorderBackupSchedulesBody = typeof reorderBackupSchedulesBody.infer;
|
export type ReorderBackupSchedulesBody = typeof reorderBackupSchedulesBody.infer;
|
||||||
|
|
@ -388,7 +388,7 @@ export const reorderBackupSchedulesResponse = type({
|
||||||
export type ReorderBackupSchedulesDto = typeof reorderBackupSchedulesResponse.infer;
|
export type ReorderBackupSchedulesDto = typeof reorderBackupSchedulesResponse.infer;
|
||||||
|
|
||||||
export const reorderBackupSchedulesDto = describeRoute({
|
export const reorderBackupSchedulesDto = describeRoute({
|
||||||
description: "Reorder backup schedules by providing an array of schedule IDs in the desired order",
|
description: "Reorder backup schedules by providing an array of schedule short IDs in the desired order",
|
||||||
operationId: "reorderBackupSchedules",
|
operationId: "reorderBackupSchedules",
|
||||||
tags: ["Backups"],
|
tags: ["Backups"],
|
||||||
responses: {
|
responses: {
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ const emitBackupStarted = (ctx: BackupContext, scheduleId: number) => {
|
||||||
|
|
||||||
serverEvents.emit("backup:started", {
|
serverEvents.emit("backup:started", {
|
||||||
organizationId: ctx.organizationId,
|
organizationId: ctx.organizationId,
|
||||||
scheduleId,
|
scheduleId: ctx.schedule.shortId,
|
||||||
volumeName: ctx.volume.name,
|
volumeName: ctx.volume.name,
|
||||||
repositoryName: ctx.repository.name,
|
repositoryName: ctx.repository.name,
|
||||||
});
|
});
|
||||||
|
|
@ -119,7 +119,7 @@ const runBackupOperation = async (ctx: BackupContext, signal: AbortSignal) => {
|
||||||
onProgress: (progress) => {
|
onProgress: (progress) => {
|
||||||
serverEvents.emit("backup:progress", {
|
serverEvents.emit("backup:progress", {
|
||||||
organizationId: ctx.organizationId,
|
organizationId: ctx.organizationId,
|
||||||
scheduleId: ctx.schedule.id,
|
scheduleId: ctx.schedule.shortId,
|
||||||
volumeName: ctx.volume.name,
|
volumeName: ctx.volume.name,
|
||||||
repositoryName: ctx.repository.name,
|
repositoryName: ctx.repository.name,
|
||||||
...progress,
|
...progress,
|
||||||
|
|
@ -173,7 +173,7 @@ const finalizeSuccessfulBackup = async (
|
||||||
|
|
||||||
serverEvents.emit("backup:completed", {
|
serverEvents.emit("backup:completed", {
|
||||||
organizationId: ctx.organizationId,
|
organizationId: ctx.organizationId,
|
||||||
scheduleId,
|
scheduleId: ctx.schedule.shortId,
|
||||||
volumeName: ctx.volume.name,
|
volumeName: ctx.volume.name,
|
||||||
repositoryName: ctx.repository.name,
|
repositoryName: ctx.repository.name,
|
||||||
status: finalStatus,
|
status: finalStatus,
|
||||||
|
|
@ -226,7 +226,7 @@ const handleBackupFailure = async (
|
||||||
|
|
||||||
serverEvents.emit("backup:completed", {
|
serverEvents.emit("backup:completed", {
|
||||||
organizationId,
|
organizationId,
|
||||||
scheduleId,
|
scheduleId: ctx.schedule.shortId,
|
||||||
volumeName: ctx.volume.name,
|
volumeName: ctx.volume.name,
|
||||||
repositoryName: ctx.repository.name,
|
repositoryName: ctx.repository.name,
|
||||||
status: "error",
|
status: "error",
|
||||||
|
|
@ -378,8 +378,8 @@ const copyToSingleMirror = async (
|
||||||
|
|
||||||
serverEvents.emit("mirror:started", {
|
serverEvents.emit("mirror:started", {
|
||||||
organizationId,
|
organizationId,
|
||||||
scheduleId,
|
scheduleId: schedule.shortId,
|
||||||
repositoryId: mirror.repositoryId,
|
repositoryId: mirror.repository.shortId,
|
||||||
repositoryName: mirror.repository.name,
|
repositoryName: mirror.repository.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -417,8 +417,8 @@ const copyToSingleMirror = async (
|
||||||
|
|
||||||
serverEvents.emit("mirror:completed", {
|
serverEvents.emit("mirror:completed", {
|
||||||
organizationId,
|
organizationId,
|
||||||
scheduleId,
|
scheduleId: schedule.shortId,
|
||||||
repositoryId: mirror.repositoryId,
|
repositoryId: mirror.repository.shortId,
|
||||||
repositoryName: mirror.repository.name,
|
repositoryName: mirror.repository.name,
|
||||||
status: "success",
|
status: "success",
|
||||||
});
|
});
|
||||||
|
|
@ -434,8 +434,8 @@ const copyToSingleMirror = async (
|
||||||
|
|
||||||
serverEvents.emit("mirror:completed", {
|
serverEvents.emit("mirror:completed", {
|
||||||
organizationId,
|
organizationId,
|
||||||
scheduleId,
|
scheduleId: schedule.shortId,
|
||||||
repositoryId: mirror.repositoryId,
|
repositoryId: mirror.repository.shortId,
|
||||||
repositoryName: mirror.repository.name,
|
repositoryName: mirror.repository.name,
|
||||||
status: "error",
|
status: "error",
|
||||||
error: errorMessage,
|
error: errorMessage,
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ const createSchedule = async (data: CreateBackupScheduleBody) => {
|
||||||
|
|
||||||
const volume = await db.query.volumesTable.findFirst({
|
const volume = await db.query.volumesTable.findFirst({
|
||||||
where: {
|
where: {
|
||||||
AND: [{ id: data.volumeId }, { organizationId }],
|
AND: [{ OR: [{ id: Number(data.volumeId) }, { shortId: String(data.volumeId) }] }, { organizationId }],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ const createSchedule = async (data: CreateBackupScheduleBody) => {
|
||||||
|
|
||||||
const repository = await db.query.repositoriesTable.findFirst({
|
const repository = await db.query.repositoriesTable.findFirst({
|
||||||
where: {
|
where: {
|
||||||
AND: [{ id: data.repositoryId }, { organizationId }],
|
AND: [{ OR: [{ id: data.repositoryId }, { shortId: data.repositoryId }] }, { organizationId }],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -118,8 +118,8 @@ const createSchedule = async (data: CreateBackupScheduleBody) => {
|
||||||
.insert(backupSchedulesTable)
|
.insert(backupSchedulesTable)
|
||||||
.values({
|
.values({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
volumeId: data.volumeId,
|
volumeId: volume.id,
|
||||||
repositoryId: data.repositoryId,
|
repositoryId: repository.id,
|
||||||
enabled: data.enabled,
|
enabled: data.enabled,
|
||||||
cronExpression: data.cronExpression,
|
cronExpression: data.cronExpression,
|
||||||
retentionPolicy: data.retentionPolicy ?? null,
|
retentionPolicy: data.retentionPolicy ?? null,
|
||||||
|
|
@ -140,11 +140,14 @@ const createSchedule = async (data: CreateBackupScheduleBody) => {
|
||||||
return newSchedule;
|
return newSchedule;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody) => {
|
const updateSchedule = async (scheduleIdOrShortId: number | string, data: UpdateBackupScheduleBody) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const schedule = await db.query.backupSchedulesTable.findFirst({
|
const schedule = await db.query.backupSchedulesTable.findFirst({
|
||||||
where: {
|
where: {
|
||||||
AND: [{ id: scheduleId }, { organizationId }],
|
AND: [
|
||||||
|
{ OR: [{ id: Number(scheduleIdOrShortId) }, { shortId: String(scheduleIdOrShortId) }] },
|
||||||
|
{ organizationId },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -159,7 +162,7 @@ const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody
|
||||||
if (data.name) {
|
if (data.name) {
|
||||||
const existingName = await db.query.backupSchedulesTable.findFirst({
|
const existingName = await db.query.backupSchedulesTable.findFirst({
|
||||||
where: {
|
where: {
|
||||||
AND: [{ name: data.name }, { NOT: { id: scheduleId } }, { organizationId }],
|
AND: [{ name: data.name }, { NOT: { id: schedule.id } }, { organizationId }],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -170,7 +173,7 @@ const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody
|
||||||
|
|
||||||
const repository = await db.query.repositoriesTable.findFirst({
|
const repository = await db.query.repositoriesTable.findFirst({
|
||||||
where: {
|
where: {
|
||||||
AND: [{ id: data.repositoryId }, { organizationId }],
|
AND: [{ OR: [{ id: data.repositoryId }, { shortId: data.repositoryId }] }, { organizationId }],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -183,8 +186,8 @@ const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody
|
||||||
|
|
||||||
const [updated] = await db
|
const [updated] = await db
|
||||||
.update(backupSchedulesTable)
|
.update(backupSchedulesTable)
|
||||||
.set({ ...data, nextBackupAt, updatedAt: Date.now() })
|
.set({ ...data, repositoryId: repository.id, nextBackupAt, updatedAt: Date.now() })
|
||||||
.where(and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)))
|
.where(and(eq(backupSchedulesTable.id, schedule.id), eq(backupSchedulesTable.organizationId, organizationId)))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (!updated) {
|
if (!updated) {
|
||||||
|
|
@ -194,11 +197,14 @@ const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody
|
||||||
return updated;
|
return updated;
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteSchedule = async (scheduleId: number) => {
|
const deleteSchedule = async (scheduleIdOrShortId: number | string) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const schedule = await db.query.backupSchedulesTable.findFirst({
|
const schedule = await db.query.backupSchedulesTable.findFirst({
|
||||||
where: {
|
where: {
|
||||||
AND: [{ id: scheduleId }, { organizationId }],
|
AND: [
|
||||||
|
{ OR: [{ id: Number(scheduleIdOrShortId) }, { shortId: String(scheduleIdOrShortId) }] },
|
||||||
|
{ organizationId },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -208,13 +214,26 @@ const deleteSchedule = async (scheduleId: number) => {
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.delete(backupSchedulesTable)
|
.delete(backupSchedulesTable)
|
||||||
.where(and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)));
|
.where(and(eq(backupSchedulesTable.id, schedule.id), eq(backupSchedulesTable.organizationId, organizationId)));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getScheduleForVolume = async (volumeId: number) => {
|
const getScheduleForVolume = async (volumeIdOrShortId: number | string) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
|
const volume = await db.query.volumesTable.findFirst({
|
||||||
|
where: {
|
||||||
|
AND: [{ OR: [{ id: Number(volumeIdOrShortId) }, { shortId: String(volumeIdOrShortId) }] }, { organizationId }],
|
||||||
|
},
|
||||||
|
columns: { id: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!volume) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const schedule = await db.query.backupSchedulesTable.findFirst({
|
const schedule = await db.query.backupSchedulesTable.findFirst({
|
||||||
where: { AND: [{ volumeId }, { organizationId }] },
|
where: {
|
||||||
|
AND: [{ volumeId: volume.id }, { organizationId }],
|
||||||
|
},
|
||||||
with: { volume: true, repository: true },
|
with: { volume: true, repository: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -225,11 +244,14 @@ const getScheduleForVolume = async (volumeId: number) => {
|
||||||
return schedule ?? null;
|
return schedule ?? null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMirrors = async (scheduleId: number) => {
|
const getMirrors = async (scheduleIdOrShortId: number | string) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const schedule = await db.query.backupSchedulesTable.findFirst({
|
const schedule = await db.query.backupSchedulesTable.findFirst({
|
||||||
where: {
|
where: {
|
||||||
AND: [{ id: scheduleId }, { organizationId }],
|
AND: [
|
||||||
|
{ OR: [{ id: Number(scheduleIdOrShortId) }, { shortId: String(scheduleIdOrShortId) }] },
|
||||||
|
{ organizationId },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -239,18 +261,33 @@ const getMirrors = async (scheduleId: number) => {
|
||||||
|
|
||||||
const mirrors = await db.query.backupScheduleMirrorsTable.findMany({
|
const mirrors = await db.query.backupScheduleMirrorsTable.findMany({
|
||||||
where: {
|
where: {
|
||||||
scheduleId,
|
scheduleId: schedule.id,
|
||||||
},
|
},
|
||||||
with: { repository: true },
|
with: { repository: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
return mirrors;
|
return mirrors.map((mirror) => ({
|
||||||
|
id: mirror.id,
|
||||||
|
scheduleId: schedule.shortId,
|
||||||
|
repositoryId: mirror.repository.shortId,
|
||||||
|
enabled: mirror.enabled,
|
||||||
|
lastCopyAt: mirror.lastCopyAt,
|
||||||
|
lastCopyStatus: mirror.lastCopyStatus,
|
||||||
|
lastCopyError: mirror.lastCopyError,
|
||||||
|
createdAt: mirror.createdAt,
|
||||||
|
repository: mirror.repository,
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateMirrors = async (scheduleId: number, data: UpdateScheduleMirrorsBody) => {
|
const updateMirrors = async (scheduleIdOrShortId: number | string, data: UpdateScheduleMirrorsBody) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const schedule = await db.query.backupSchedulesTable.findFirst({
|
const schedule = await db.query.backupSchedulesTable.findFirst({
|
||||||
where: { AND: [{ id: scheduleId }, { organizationId }] },
|
where: {
|
||||||
|
AND: [
|
||||||
|
{ OR: [{ id: Number(scheduleIdOrShortId) }, { shortId: String(scheduleIdOrShortId) }] },
|
||||||
|
{ organizationId },
|
||||||
|
],
|
||||||
|
},
|
||||||
with: { repository: true },
|
with: { repository: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -258,30 +295,36 @@ const updateMirrors = async (scheduleId: number, data: UpdateScheduleMirrorsBody
|
||||||
throw new NotFoundError("Backup schedule not found");
|
throw new NotFoundError("Backup schedule not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const mirror of data.mirrors) {
|
const normalizedMirrors = await Promise.all(
|
||||||
if (mirror.repositoryId === schedule.repositoryId) {
|
data.mirrors.map(async (mirror) => {
|
||||||
throw new BadRequestError("Cannot add the primary repository as a mirror");
|
const repo = await db.query.repositoriesTable.findFirst({
|
||||||
}
|
where: {
|
||||||
|
AND: [{ OR: [{ id: mirror.repositoryId }, { shortId: mirror.repositoryId }] }, { organizationId }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const repo = await db.query.repositoriesTable.findFirst({
|
if (!repo) {
|
||||||
where: { AND: [{ id: mirror.repositoryId }, { organizationId }] },
|
throw new NotFoundError(`Repository ${mirror.repositoryId} not found`);
|
||||||
});
|
}
|
||||||
|
|
||||||
if (!repo) {
|
if (repo.id === schedule.repositoryId) {
|
||||||
throw new NotFoundError(`Repository ${mirror.repositoryId} not found`);
|
throw new BadRequestError("Cannot add the primary repository as a mirror");
|
||||||
}
|
}
|
||||||
|
|
||||||
const compatibility = await checkMirrorCompatibility(schedule.repository.config, repo.config, repo.id);
|
const compatibility = await checkMirrorCompatibility(schedule.repository.config, repo.config, repo.id);
|
||||||
|
|
||||||
if (!compatibility.compatible) {
|
if (!compatibility.compatible) {
|
||||||
throw new BadRequestError(
|
throw new BadRequestError(
|
||||||
getIncompatibleMirrorError(repo.name, schedule.repository.config.backend, repo.config.backend),
|
getIncompatibleMirrorError(repo.name, schedule.repository.config.backend, repo.config.backend),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return { repositoryId: repo.id, enabled: mirror.enabled };
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const existingMirrors = await db.query.backupScheduleMirrorsTable.findMany({
|
const existingMirrors = await db.query.backupScheduleMirrorsTable.findMany({
|
||||||
where: { scheduleId },
|
where: { scheduleId: schedule.id },
|
||||||
});
|
});
|
||||||
|
|
||||||
const existingMirrorsMap = new Map(
|
const existingMirrorsMap = new Map(
|
||||||
|
|
@ -291,14 +334,14 @@ const updateMirrors = async (scheduleId: number, data: UpdateScheduleMirrorsBody
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
await db.delete(backupScheduleMirrorsTable).where(eq(backupScheduleMirrorsTable.scheduleId, scheduleId));
|
await db.delete(backupScheduleMirrorsTable).where(eq(backupScheduleMirrorsTable.scheduleId, schedule.id));
|
||||||
|
|
||||||
if (data.mirrors.length > 0) {
|
if (normalizedMirrors.length > 0) {
|
||||||
await db.insert(backupScheduleMirrorsTable).values(
|
await db.insert(backupScheduleMirrorsTable).values(
|
||||||
data.mirrors.map((mirror) => {
|
normalizedMirrors.map((mirror) => {
|
||||||
const existing = existingMirrorsMap.get(mirror.repositoryId);
|
const existing = existingMirrorsMap.get(mirror.repositoryId);
|
||||||
return {
|
return {
|
||||||
scheduleId,
|
scheduleId: schedule.id,
|
||||||
repositoryId: mirror.repositoryId,
|
repositoryId: mirror.repositoryId,
|
||||||
enabled: mirror.enabled,
|
enabled: mirror.enabled,
|
||||||
lastCopyAt: existing?.lastCopyAt ?? null,
|
lastCopyAt: existing?.lastCopyAt ?? null,
|
||||||
|
|
@ -309,13 +352,18 @@ const updateMirrors = async (scheduleId: number, data: UpdateScheduleMirrorsBody
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return getMirrors(scheduleId);
|
return getMirrors(schedule.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMirrorCompatibility = async (scheduleId: number) => {
|
const getMirrorCompatibility = async (scheduleIdOrShortId: number | string) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const schedule = await db.query.backupSchedulesTable.findFirst({
|
const schedule = await db.query.backupSchedulesTable.findFirst({
|
||||||
where: { AND: [{ id: scheduleId }, { organizationId }] },
|
where: {
|
||||||
|
AND: [
|
||||||
|
{ OR: [{ id: Number(scheduleIdOrShortId) }, { shortId: String(scheduleIdOrShortId) }] },
|
||||||
|
{ organizationId },
|
||||||
|
],
|
||||||
|
},
|
||||||
with: { repository: true },
|
with: { repository: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -327,29 +375,33 @@ const getMirrorCompatibility = async (scheduleId: number) => {
|
||||||
const repos = allRepositories.filter((repo) => repo.id !== schedule.repositoryId);
|
const repos = allRepositories.filter((repo) => repo.id !== schedule.repositoryId);
|
||||||
|
|
||||||
const compatibility = await Promise.all(
|
const compatibility = await Promise.all(
|
||||||
repos.map((repo) => checkMirrorCompatibility(schedule.repository.config, repo.config, repo.id)),
|
repos.map((repo) => checkMirrorCompatibility(schedule.repository.config, repo.config, repo.shortId)),
|
||||||
);
|
);
|
||||||
|
|
||||||
return compatibility;
|
return compatibility;
|
||||||
};
|
};
|
||||||
|
|
||||||
const reorderSchedules = async (scheduleIds: number[]) => {
|
const reorderSchedules = async (scheduleShortIds: string[]) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const uniqueIds = new Set(scheduleIds);
|
const uniqueIds = new Set(scheduleShortIds);
|
||||||
if (uniqueIds.size !== scheduleIds.length) {
|
if (uniqueIds.size !== scheduleShortIds.length) {
|
||||||
throw new BadRequestError("Duplicate schedule IDs in reorder request");
|
throw new BadRequestError("Duplicate schedule IDs in reorder request");
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingSchedules = await db.query.backupSchedulesTable.findMany({
|
const existingSchedules = await db.query.backupSchedulesTable.findMany({
|
||||||
where: { organizationId },
|
where: { organizationId },
|
||||||
columns: { id: true },
|
columns: { id: true, shortId: true },
|
||||||
});
|
});
|
||||||
const existingIds = new Set(existingSchedules.map((s) => s.id));
|
|
||||||
|
|
||||||
for (const id of scheduleIds) {
|
const shortIdToId = new Map(existingSchedules.map((s) => [s.shortId, s.id]));
|
||||||
if (!existingIds.has(id)) {
|
|
||||||
throw new NotFoundError(`Backup schedule with ID ${id} not found`);
|
const scheduleIds: number[] = [];
|
||||||
|
for (const shortId of scheduleShortIds) {
|
||||||
|
const id = shortIdToId.get(shortId);
|
||||||
|
if (id === undefined) {
|
||||||
|
throw new NotFoundError(`Backup schedule with short ID ${shortId} not found`);
|
||||||
}
|
}
|
||||||
|
scheduleIds.push(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
db.transaction((tx) => {
|
db.transaction((tx) => {
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@ describe("repositories updates", () => {
|
||||||
const { token, organizationId } = await createTestSession();
|
const { token, organizationId } = await createTestSession();
|
||||||
const repository = await createRepositoryRecord(organizationId);
|
const repository = await createRepositoryRecord(organizationId);
|
||||||
|
|
||||||
const res = await app.request(`/api/v1/repositories/${repository.id}`, {
|
const res = await app.request(`/api/v1/repositories/${repository.shortId}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: {
|
headers: {
|
||||||
...getAuthHeaders(token),
|
...getAuthHeaders(token),
|
||||||
|
|
@ -212,7 +212,7 @@ describe("repositories updates", () => {
|
||||||
const { token, organizationId } = await createTestSession();
|
const { token, organizationId } = await createTestSession();
|
||||||
const repository = await createRepositoryRecord(organizationId);
|
const repository = await createRepositoryRecord(organizationId);
|
||||||
|
|
||||||
const res = await app.request(`/api/v1/repositories/${repository.id}`, {
|
const res = await app.request(`/api/v1/repositories/${repository.shortId}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: {
|
headers: {
|
||||||
...getAuthHeaders(token),
|
...getAuthHeaders(token),
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ const saveDoctorResults = async (repositoryId: string, steps: DoctorStep[], fina
|
||||||
|
|
||||||
export const executeDoctor = async (
|
export const executeDoctor = async (
|
||||||
repositoryId: string,
|
repositoryId: string,
|
||||||
|
repositoryShortId: string,
|
||||||
repositoryConfig: RepositoryConfig,
|
repositoryConfig: RepositoryConfig,
|
||||||
repositoryName: string,
|
repositoryName: string,
|
||||||
signal: AbortSignal,
|
signal: AbortSignal,
|
||||||
|
|
@ -153,7 +154,7 @@ export const executeDoctor = async (
|
||||||
|
|
||||||
serverEvents.emit("doctor:completed", {
|
serverEvents.emit("doctor:completed", {
|
||||||
organizationId,
|
organizationId,
|
||||||
repositoryId,
|
repositoryId: repositoryShortId,
|
||||||
repositoryName,
|
repositoryName,
|
||||||
success: steps.every((s) => s.success),
|
success: steps.every((s) => s.success),
|
||||||
steps,
|
steps,
|
||||||
|
|
@ -179,7 +180,7 @@ export const executeDoctor = async (
|
||||||
|
|
||||||
serverEvents.emit("doctor:cancelled", {
|
serverEvents.emit("doctor:cancelled", {
|
||||||
organizationId,
|
organizationId,
|
||||||
repositoryId,
|
repositoryId: repositoryShortId,
|
||||||
repositoryName,
|
repositoryName,
|
||||||
error: toMessage(error),
|
error: toMessage(error),
|
||||||
});
|
});
|
||||||
|
|
@ -192,7 +193,7 @@ export const executeDoctor = async (
|
||||||
steps.push({ step: "doctor", success: false, output: null, error: toMessage(error) });
|
steps.push({ step: "doctor", success: false, output: null, error: toMessage(error) });
|
||||||
serverEvents.emit("doctor:completed", {
|
serverEvents.emit("doctor:completed", {
|
||||||
organizationId,
|
organizationId,
|
||||||
repositoryId,
|
repositoryId: repositoryShortId,
|
||||||
repositoryName,
|
repositoryName,
|
||||||
success: false,
|
success: false,
|
||||||
steps,
|
steps,
|
||||||
|
|
|
||||||
|
|
@ -79,25 +79,25 @@ export const repositoriesController = new Hono()
|
||||||
|
|
||||||
return c.json(remotes);
|
return c.json(remotes);
|
||||||
})
|
})
|
||||||
.get("/:id", getRepositoryDto, async (c) => {
|
.get("/:shortId", getRepositoryDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const res = await repositoriesService.getRepository(id);
|
const res = await repositoriesService.getRepository(shortId);
|
||||||
|
|
||||||
return c.json<GetRepositoryDto>(res.repository, 200);
|
return c.json<GetRepositoryDto>(res.repository, 200);
|
||||||
})
|
})
|
||||||
.delete("/:id", deleteRepositoryDto, async (c) => {
|
.delete("/:shortId", deleteRepositoryDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
await repositoriesService.deleteRepository(id);
|
await repositoriesService.deleteRepository(shortId);
|
||||||
|
|
||||||
return c.json<DeleteRepositoryDto>({ message: "Repository deleted" }, 200);
|
return c.json<DeleteRepositoryDto>({ message: "Repository deleted" }, 200);
|
||||||
})
|
})
|
||||||
.get("/:id/snapshots", listSnapshotsDto, validator("query", listSnapshotsFilters), async (c) => {
|
.get("/:shortId/snapshots", listSnapshotsDto, validator("query", listSnapshotsFilters), async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const { backupId } = c.req.valid("query");
|
const { backupId } = c.req.valid("query");
|
||||||
|
|
||||||
const [res, retentionCategories] = await Promise.all([
|
const [res, retentionCategories] = await Promise.all([
|
||||||
repositoriesService.listSnapshots(id, backupId),
|
repositoriesService.listSnapshots(shortId, backupId),
|
||||||
repositoriesService.getRetentionCategories(id, backupId),
|
repositoriesService.getRetentionCategories(shortId, backupId),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const snapshots = res.map((snapshot) => {
|
const snapshots = res.map((snapshot) => {
|
||||||
|
|
@ -119,15 +119,15 @@ export const repositoriesController = new Hono()
|
||||||
|
|
||||||
return c.json<ListSnapshotsDto>(snapshots, 200);
|
return c.json<ListSnapshotsDto>(snapshots, 200);
|
||||||
})
|
})
|
||||||
.post("/:id/snapshots/refresh", refreshSnapshotsDto, async (c) => {
|
.post("/:shortId/snapshots/refresh", refreshSnapshotsDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const result = await repositoriesService.refreshSnapshots(id);
|
const result = await repositoriesService.refreshSnapshots(shortId);
|
||||||
|
|
||||||
return c.json<RefreshSnapshotsDto>(result, 200);
|
return c.json<RefreshSnapshotsDto>(result, 200);
|
||||||
})
|
})
|
||||||
.get("/:id/snapshots/:snapshotId", getSnapshotDetailsDto, async (c) => {
|
.get("/:shortId/snapshots/:snapshotId", getSnapshotDetailsDto, async (c) => {
|
||||||
const { id, snapshotId } = c.req.param();
|
const { shortId, snapshotId } = c.req.param();
|
||||||
const snapshot = await repositoriesService.getSnapshotDetails(id, snapshotId);
|
const snapshot = await repositoriesService.getSnapshotDetails(shortId, snapshotId);
|
||||||
|
|
||||||
const duration = getSnapshotDuration(snapshot.summary);
|
const duration = getSnapshotDuration(snapshot.summary);
|
||||||
|
|
||||||
|
|
@ -146,11 +146,11 @@ export const repositoriesController = new Hono()
|
||||||
return c.json<GetSnapshotDetailsDto>(response, 200);
|
return c.json<GetSnapshotDetailsDto>(response, 200);
|
||||||
})
|
})
|
||||||
.get(
|
.get(
|
||||||
"/:id/snapshots/:snapshotId/files",
|
"/:shortId/snapshots/:snapshotId/files",
|
||||||
listSnapshotFilesDto,
|
listSnapshotFilesDto,
|
||||||
validator("query", listSnapshotFilesQuery),
|
validator("query", listSnapshotFilesQuery),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const { id, snapshotId } = c.req.param();
|
const { shortId, snapshotId } = c.req.param();
|
||||||
const { path, ...query } = c.req.valid("query");
|
const { path, ...query } = c.req.valid("query");
|
||||||
|
|
||||||
const decodedPath = path ? decodeURIComponent(path) : undefined;
|
const decodedPath = path ? decodeURIComponent(path) : undefined;
|
||||||
|
|
@ -158,76 +158,76 @@ export const repositoriesController = new Hono()
|
||||||
const offset = Math.max(0, Number.parseInt(query.offset ?? "0", 10) || 0);
|
const offset = Math.max(0, Number.parseInt(query.offset ?? "0", 10) || 0);
|
||||||
const limit = Math.min(1000, Math.max(1, Number.parseInt(query.limit ?? "500", 10) || 500));
|
const limit = Math.min(1000, Math.max(1, Number.parseInt(query.limit ?? "500", 10) || 500));
|
||||||
|
|
||||||
const result = await repositoriesService.listSnapshotFiles(id, snapshotId, decodedPath, { offset, limit });
|
const result = await repositoriesService.listSnapshotFiles(shortId, snapshotId, decodedPath, { offset, limit });
|
||||||
|
|
||||||
c.header("Cache-Control", "max-age=300, stale-while-revalidate=600");
|
c.header("Cache-Control", "max-age=300, stale-while-revalidate=600");
|
||||||
|
|
||||||
return c.json<ListSnapshotFilesDto>(result, 200);
|
return c.json<ListSnapshotFilesDto>(result, 200);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post("/:id/restore", restoreSnapshotDto, validator("json", restoreSnapshotBody), async (c) => {
|
.post("/:shortId/restore", restoreSnapshotDto, validator("json", restoreSnapshotBody), async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const { snapshotId, ...options } = c.req.valid("json");
|
const { snapshotId, ...options } = c.req.valid("json");
|
||||||
const result = await repositoriesService.restoreSnapshot(id, snapshotId, options);
|
const result = await repositoriesService.restoreSnapshot(shortId, snapshotId, options);
|
||||||
|
|
||||||
return c.json<RestoreSnapshotDto>(result, 200);
|
return c.json<RestoreSnapshotDto>(result, 200);
|
||||||
})
|
})
|
||||||
.post("/:id/doctor", startDoctorDto, async (c) => {
|
.post("/:shortId/doctor", startDoctorDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
|
|
||||||
const result = await repositoriesService.startDoctor(id);
|
const result = await repositoriesService.startDoctor(shortId);
|
||||||
|
|
||||||
return c.json<StartDoctorDto>(result, 202);
|
return c.json<StartDoctorDto>(result, 202);
|
||||||
})
|
})
|
||||||
.delete("/:id/doctor", cancelDoctorDto, async (c) => {
|
.delete("/:shortId/doctor", cancelDoctorDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
|
|
||||||
const result = await repositoriesService.cancelDoctor(id);
|
const result = await repositoriesService.cancelDoctor(shortId);
|
||||||
|
|
||||||
return c.json<CancelDoctorDto>(result, 200);
|
return c.json<CancelDoctorDto>(result, 200);
|
||||||
})
|
})
|
||||||
.post("/:id/unlock", unlockRepositoryDto, async (c) => {
|
.post("/:shortId/unlock", unlockRepositoryDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
|
|
||||||
const result = await repositoriesService.unlockRepository(id);
|
const result = await repositoriesService.unlockRepository(shortId);
|
||||||
|
|
||||||
return c.json<UnlockRepositoryDto>(result, 200);
|
return c.json<UnlockRepositoryDto>(result, 200);
|
||||||
})
|
})
|
||||||
.delete("/:id/snapshots/:snapshotId", deleteSnapshotDto, async (c) => {
|
.delete("/:shortId/snapshots/:snapshotId", deleteSnapshotDto, async (c) => {
|
||||||
const { id, snapshotId } = c.req.param();
|
const { shortId, snapshotId } = c.req.param();
|
||||||
await repositoriesService.deleteSnapshot(id, snapshotId);
|
await repositoriesService.deleteSnapshot(shortId, snapshotId);
|
||||||
|
|
||||||
return c.json<DeleteSnapshotDto>({ message: "Snapshot deleted" }, 200);
|
return c.json<DeleteSnapshotDto>({ message: "Snapshot deleted" }, 200);
|
||||||
})
|
})
|
||||||
.delete("/:id/snapshots", deleteSnapshotsDto, validator("json", deleteSnapshotsBody), async (c) => {
|
.delete("/:shortId/snapshots", deleteSnapshotsDto, validator("json", deleteSnapshotsBody), async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const { snapshotIds } = c.req.valid("json");
|
const { snapshotIds } = c.req.valid("json");
|
||||||
await repositoriesService.deleteSnapshots(id, snapshotIds);
|
await repositoriesService.deleteSnapshots(shortId, snapshotIds);
|
||||||
|
|
||||||
return c.json<DeleteSnapshotsResponseDto>({ message: "Snapshots deleted" }, 200);
|
return c.json<DeleteSnapshotsResponseDto>({ message: "Snapshots deleted" }, 200);
|
||||||
})
|
})
|
||||||
.post("/:id/snapshots/tag", tagSnapshotsDto, validator("json", tagSnapshotsBody), async (c) => {
|
.post("/:shortId/snapshots/tag", tagSnapshotsDto, validator("json", tagSnapshotsBody), async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const { snapshotIds, ...tags } = c.req.valid("json");
|
const { snapshotIds, ...tags } = c.req.valid("json");
|
||||||
await repositoriesService.tagSnapshots(id, snapshotIds, tags);
|
await repositoriesService.tagSnapshots(shortId, snapshotIds, tags);
|
||||||
|
|
||||||
return c.json<TagSnapshotsResponseDto>({ message: "Snapshots tagged" }, 200);
|
return c.json<TagSnapshotsResponseDto>({ message: "Snapshots tagged" }, 200);
|
||||||
})
|
})
|
||||||
.patch("/:id", updateRepositoryDto, validator("json", updateRepositoryBody), async (c) => {
|
.patch("/:shortId", updateRepositoryDto, validator("json", updateRepositoryBody), async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json");
|
||||||
const res = await repositoriesService.updateRepository(id, body);
|
const res = await repositoriesService.updateRepository(shortId, body);
|
||||||
|
|
||||||
return c.json<UpdateRepositoryDto>(res.repository, 200);
|
return c.json<UpdateRepositoryDto>(res.repository, 200);
|
||||||
})
|
})
|
||||||
.post(
|
.post(
|
||||||
"/:id/exec",
|
"/:shortId/exec",
|
||||||
requireDevPanel,
|
requireDevPanel,
|
||||||
requireOrgAdmin,
|
requireOrgAdmin,
|
||||||
devPanelExecDto,
|
devPanelExecDto,
|
||||||
validator("json", devPanelExecBody),
|
validator("json", devPanelExecBody),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json");
|
||||||
|
|
||||||
return streamSSE(c, async (stream) => {
|
return streamSSE(c, async (stream) => {
|
||||||
|
|
@ -240,7 +240,7 @@ export const repositoriesController = new Hono()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await repositoriesService.execResticCommand(
|
const result = await repositoriesService.execResticCommand(
|
||||||
id,
|
shortId,
|
||||||
body.command,
|
body.command,
|
||||||
body.args,
|
body.args,
|
||||||
async (line) => sendSSE("output", { type: "stdout", line }),
|
async (line) => sendSSE("output", { type: "stdout", line }),
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,11 @@ import { findCommonAncestor } from "~/utils/common-ancestor";
|
||||||
|
|
||||||
const runningDoctors = new Map<string, AbortController>();
|
const runningDoctors = new Map<string, AbortController>();
|
||||||
|
|
||||||
const findRepository = async (idOrShortId: string) => {
|
const findRepository = async (shortId: string) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
return await db.query.repositoriesTable.findFirst({
|
return await db.query.repositoriesTable.findFirst({
|
||||||
where: {
|
where: {
|
||||||
AND: [{ OR: [{ id: idOrShortId }, { shortId: idOrShortId }] }, { organizationId }],
|
AND: [{ shortId }, { organizationId }],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -182,8 +182,8 @@ const createRepository = async (name: string, config: RepositoryConfig, compress
|
||||||
throw new InternalServerError(`Failed to initialize repository: ${errorMessage}`);
|
throw new InternalServerError(`Failed to initialize repository: ${errorMessage}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRepository = async (id: string) => {
|
const getRepository = async (shortId: string) => {
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -192,8 +192,8 @@ const getRepository = async (id: string) => {
|
||||||
return { repository };
|
return { repository };
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteRepository = async (id: string) => {
|
const deleteRepository = async (shortId: string) => {
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -214,14 +214,14 @@ const deleteRepository = async (id: string) => {
|
||||||
/**
|
/**
|
||||||
* List snapshots for a given repository
|
* List snapshots for a given repository
|
||||||
* If backupId is provided, filter snapshots by that backup ID (tag)
|
* If backupId is provided, filter snapshots by that backup ID (tag)
|
||||||
* @param id Repository ID
|
* @param shortId Repository short ID
|
||||||
* @param backupId Optional backup ID to filter snapshots for a specific backup schedule
|
* @param backupId Optional backup ID to filter snapshots for a specific backup schedule
|
||||||
*
|
*
|
||||||
* @returns List of snapshots
|
* @returns List of snapshots
|
||||||
*/
|
*/
|
||||||
const listSnapshots = async (id: string, backupId?: string) => {
|
const listSnapshots = async (shortId: string, backupId?: string) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -252,13 +252,13 @@ const listSnapshots = async (id: string, backupId?: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const listSnapshotFiles = async (
|
const listSnapshotFiles = async (
|
||||||
id: string,
|
shortId: string,
|
||||||
snapshotId: string,
|
snapshotId: string,
|
||||||
path?: string,
|
path?: string,
|
||||||
options?: { offset?: number; limit?: number },
|
options?: { offset?: number; limit?: number },
|
||||||
) => {
|
) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -317,7 +317,7 @@ const listSnapshotFiles = async (
|
||||||
};
|
};
|
||||||
|
|
||||||
const restoreSnapshot = async (
|
const restoreSnapshot = async (
|
||||||
id: string,
|
shortId: string,
|
||||||
snapshotId: string,
|
snapshotId: string,
|
||||||
options?: {
|
options?: {
|
||||||
include?: string[];
|
include?: string[];
|
||||||
|
|
@ -329,7 +329,7 @@ const restoreSnapshot = async (
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -337,14 +337,14 @@ const restoreSnapshot = async (
|
||||||
|
|
||||||
const target = options?.targetPath || "/";
|
const target = options?.targetPath || "/";
|
||||||
|
|
||||||
const { paths } = await getSnapshotDetails(repository.id, snapshotId);
|
const { paths } = await getSnapshotDetails(repository.shortId, snapshotId);
|
||||||
const basePath = findCommonAncestor(paths);
|
const basePath = findCommonAncestor(paths);
|
||||||
|
|
||||||
const releaseLock = await repoMutex.acquireShared(repository.id, `restore:${snapshotId}`);
|
const releaseLock = await repoMutex.acquireShared(repository.id, `restore:${snapshotId}`);
|
||||||
try {
|
try {
|
||||||
serverEvents.emit("restore:started", {
|
serverEvents.emit("restore:started", {
|
||||||
organizationId,
|
organizationId,
|
||||||
repositoryId: repository.id,
|
repositoryId: repository.shortId,
|
||||||
snapshotId,
|
snapshotId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -355,7 +355,7 @@ const restoreSnapshot = async (
|
||||||
onProgress: (progress) => {
|
onProgress: (progress) => {
|
||||||
serverEvents.emit("restore:progress", {
|
serverEvents.emit("restore:progress", {
|
||||||
organizationId,
|
organizationId,
|
||||||
repositoryId: repository.id,
|
repositoryId: repository.shortId,
|
||||||
snapshotId,
|
snapshotId,
|
||||||
...progress,
|
...progress,
|
||||||
});
|
});
|
||||||
|
|
@ -364,7 +364,7 @@ const restoreSnapshot = async (
|
||||||
|
|
||||||
serverEvents.emit("restore:completed", {
|
serverEvents.emit("restore:completed", {
|
||||||
organizationId,
|
organizationId,
|
||||||
repositoryId: repository.id,
|
repositoryId: repository.shortId,
|
||||||
snapshotId,
|
snapshotId,
|
||||||
status: "success",
|
status: "success",
|
||||||
});
|
});
|
||||||
|
|
@ -378,7 +378,7 @@ const restoreSnapshot = async (
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
serverEvents.emit("restore:completed", {
|
serverEvents.emit("restore:completed", {
|
||||||
organizationId,
|
organizationId,
|
||||||
repositoryId: repository.id,
|
repositoryId: repository.shortId,
|
||||||
snapshotId,
|
snapshotId,
|
||||||
status: "error",
|
status: "error",
|
||||||
error: toMessage(error),
|
error: toMessage(error),
|
||||||
|
|
@ -389,9 +389,9 @@ const restoreSnapshot = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSnapshotDetails = async (id: string, snapshotId: string) => {
|
const getSnapshotDetails = async (shortId: string, snapshotId: string) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -413,7 +413,7 @@ const getSnapshotDetails = async (id: string, snapshotId: string) => {
|
||||||
const snapshot = snapshots.find((snap) => snap.id === snapshotId || snap.short_id === snapshotId);
|
const snapshot = snapshots.find((snap) => snap.id === snapshotId || snap.short_id === snapshotId);
|
||||||
|
|
||||||
if (!snapshot) {
|
if (!snapshot) {
|
||||||
void refreshSnapshots(id).catch(() => {});
|
void refreshSnapshots(shortId).catch(() => {});
|
||||||
|
|
||||||
throw new NotFoundError("Snapshot not found");
|
throw new NotFoundError("Snapshot not found");
|
||||||
}
|
}
|
||||||
|
|
@ -450,8 +450,8 @@ const checkHealth = async (repositoryId: string) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const startDoctor = async (id: string) => {
|
const startDoctor = async (shortId: string) => {
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -468,7 +468,7 @@ const startDoctor = async (id: string) => {
|
||||||
|
|
||||||
serverEvents.emit("doctor:started", {
|
serverEvents.emit("doctor:started", {
|
||||||
organizationId: repository.organizationId,
|
organizationId: repository.organizationId,
|
||||||
repositoryId: repository.id,
|
repositoryId: repository.shortId,
|
||||||
repositoryName: repository.name,
|
repositoryName: repository.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -478,7 +478,7 @@ const startDoctor = async (id: string) => {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
executeDoctor(repository.id, repository.config, repository.name, abortController.signal)
|
executeDoctor(repository.id, repository.shortId, repository.config, repository.name, abortController.signal)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
logger.error(`Doctor background task failed: ${toMessage(error)}`);
|
logger.error(`Doctor background task failed: ${toMessage(error)}`);
|
||||||
})
|
})
|
||||||
|
|
@ -486,11 +486,11 @@ const startDoctor = async (id: string) => {
|
||||||
runningDoctors.delete(repository.id);
|
runningDoctors.delete(repository.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
return { message: "Doctor operation started", repositoryId: repository.id };
|
return { message: "Doctor operation started", repositoryId: repository.shortId };
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelDoctor = async (id: string) => {
|
const cancelDoctor = async (shortId: string) => {
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -509,16 +509,16 @@ const cancelDoctor = async (id: string) => {
|
||||||
|
|
||||||
serverEvents.emit("doctor:cancelled", {
|
serverEvents.emit("doctor:cancelled", {
|
||||||
organizationId: repository.organizationId,
|
organizationId: repository.organizationId,
|
||||||
repositoryId: repository.id,
|
repositoryId: repository.shortId,
|
||||||
repositoryName: repository.name,
|
repositoryName: repository.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { message: "Doctor operation cancelled" };
|
return { message: "Doctor operation cancelled" };
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteSnapshot = async (id: string, snapshotId: string) => {
|
const deleteSnapshot = async (shortId: string, snapshotId: string) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -534,9 +534,9 @@ const deleteSnapshot = async (id: string, snapshotId: string) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteSnapshots = async (id: string, snapshotIds: string[]) => {
|
const deleteSnapshots = async (shortId: string, snapshotIds: string[]) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -555,12 +555,12 @@ const deleteSnapshots = async (id: string, snapshotIds: string[]) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const tagSnapshots = async (
|
const tagSnapshots = async (
|
||||||
id: string,
|
shortId: string,
|
||||||
snapshotIds: string[],
|
snapshotIds: string[],
|
||||||
tags: { add?: string[]; remove?: string[]; set?: string[] },
|
tags: { add?: string[]; remove?: string[]; set?: string[] },
|
||||||
) => {
|
) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -578,9 +578,9 @@ const tagSnapshots = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshSnapshots = async (id: string) => {
|
const refreshSnapshots = async (shortId: string) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -604,8 +604,8 @@ const refreshSnapshots = async (id: string) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateRepository = async (id: string, updates: UpdateRepositoryBody) => {
|
const updateRepository = async (shortId: string, updates: UpdateRepositoryBody) => {
|
||||||
const existing = await findRepository(id);
|
const existing = await findRepository(shortId);
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -676,9 +676,9 @@ const updateRepository = async (id: string, updates: UpdateRepositoryBody) => {
|
||||||
return { repository: updated };
|
return { repository: updated };
|
||||||
};
|
};
|
||||||
|
|
||||||
const unlockRepository = async (id: string) => {
|
const unlockRepository = async (shortId: string) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
@ -694,7 +694,7 @@ const unlockRepository = async (id: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const execResticCommand = async (
|
const execResticCommand = async (
|
||||||
id: string,
|
shortId: string,
|
||||||
command: string,
|
command: string,
|
||||||
args: string[] | undefined,
|
args: string[] | undefined,
|
||||||
onStdout: (line: string) => void,
|
onStdout: (line: string) => void,
|
||||||
|
|
@ -702,7 +702,7 @@ const execResticCommand = async (
|
||||||
signal?: AbortSignal,
|
signal?: AbortSignal,
|
||||||
) => {
|
) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const repository = await findRepository(id);
|
const repository = await findRepository(shortId);
|
||||||
|
|
||||||
if (!repository) {
|
if (!repository) {
|
||||||
throw new NotFoundError("Repository not found");
|
throw new NotFoundError("Repository not found");
|
||||||
|
|
|
||||||
|
|
@ -51,15 +51,15 @@ export const volumeController = new Hono()
|
||||||
|
|
||||||
return c.json(result, 200);
|
return c.json(result, 200);
|
||||||
})
|
})
|
||||||
.delete("/:id", deleteVolumeDto, async (c) => {
|
.delete("/:shortId", deleteVolumeDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
await volumeService.deleteVolume(id);
|
await volumeService.deleteVolume(shortId);
|
||||||
|
|
||||||
return c.json({ message: "Volume deleted" }, 200);
|
return c.json({ message: "Volume deleted" }, 200);
|
||||||
})
|
})
|
||||||
.get("/:id", getVolumeDto, async (c) => {
|
.get("/:shortId", getVolumeDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const res = await volumeService.getVolume(id);
|
const res = await volumeService.getVolume(shortId);
|
||||||
|
|
||||||
const response = {
|
const response = {
|
||||||
volume: {
|
volume: {
|
||||||
|
|
@ -75,10 +75,10 @@ export const volumeController = new Hono()
|
||||||
|
|
||||||
return c.json<GetVolumeDto>(response, 200);
|
return c.json<GetVolumeDto>(response, 200);
|
||||||
})
|
})
|
||||||
.put("/:id", updateVolumeDto, validator("json", updateVolumeBody), async (c) => {
|
.put("/:shortId", updateVolumeDto, validator("json", updateVolumeBody), async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json");
|
||||||
const res = await volumeService.updateVolume(id, body);
|
const res = await volumeService.updateVolume(shortId, body);
|
||||||
|
|
||||||
const response = {
|
const response = {
|
||||||
...res.volume,
|
...res.volume,
|
||||||
|
|
@ -87,32 +87,32 @@ export const volumeController = new Hono()
|
||||||
|
|
||||||
return c.json<UpdateVolumeDto>(response, 200);
|
return c.json<UpdateVolumeDto>(response, 200);
|
||||||
})
|
})
|
||||||
.post("/:id/mount", mountVolumeDto, async (c) => {
|
.post("/:shortId/mount", mountVolumeDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const { error, status } = await volumeService.mountVolume(id);
|
const { error, status } = await volumeService.mountVolume(shortId);
|
||||||
|
|
||||||
return c.json({ error, status }, error ? 500 : 200);
|
return c.json({ error, status }, error ? 500 : 200);
|
||||||
})
|
})
|
||||||
.post("/:id/unmount", unmountVolumeDto, async (c) => {
|
.post("/:shortId/unmount", unmountVolumeDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const { error, status } = await volumeService.unmountVolume(id);
|
const { error, status } = await volumeService.unmountVolume(shortId);
|
||||||
|
|
||||||
return c.json({ error, status }, error ? 500 : 200);
|
return c.json({ error, status }, error ? 500 : 200);
|
||||||
})
|
})
|
||||||
.post("/:id/health-check", healthCheckDto, async (c) => {
|
.post("/:shortId/health-check", healthCheckDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const { error, status } = await volumeService.checkHealth(id);
|
const { error, status } = await volumeService.checkHealth(shortId);
|
||||||
|
|
||||||
return c.json({ error, status }, 200);
|
return c.json({ error, status }, 200);
|
||||||
})
|
})
|
||||||
.get("/:id/files", validator("query", listFilesQuery), listFilesDto, async (c) => {
|
.get("/:shortId/files", validator("query", listFilesQuery), listFilesDto, async (c) => {
|
||||||
const { id } = c.req.param();
|
const { shortId } = c.req.param();
|
||||||
const { path, ...query } = c.req.valid("query");
|
const { path, ...query } = c.req.valid("query");
|
||||||
|
|
||||||
const offset = Math.max(0, Number.parseInt(query.offset ?? "0", 10) || 0);
|
const offset = Math.max(0, Number.parseInt(query.offset ?? "0", 10) || 0);
|
||||||
const limit = Math.min(1000, Math.max(1, Number.parseInt(query.limit ?? "500", 10) || 500));
|
const limit = Math.min(1000, Math.max(1, Number.parseInt(query.limit ?? "500", 10) || 500));
|
||||||
|
|
||||||
const result = await volumeService.listFiles(id, path, offset, limit);
|
const result = await volumeService.listFiles(shortId, path, offset, limit);
|
||||||
|
|
||||||
const response = {
|
const response = {
|
||||||
files: result.files,
|
files: result.files,
|
||||||
|
|
|
||||||
|
|
@ -52,14 +52,11 @@ const listVolumes = async () => {
|
||||||
return volumes;
|
return volumes;
|
||||||
};
|
};
|
||||||
|
|
||||||
const findVolume = async (idOrShortId: string | number) => {
|
const findVolume = async (identifier: string | number) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
return await db.query.volumesTable.findFirst({
|
return await db.query.volumesTable.findFirst({
|
||||||
where: {
|
where: {
|
||||||
AND: [
|
AND: [{ OR: [{ id: Number(identifier) }, { shortId: String(identifier) }] }, { organizationId: organizationId }],
|
||||||
{ OR: [{ id: Number(idOrShortId) }, { shortId: String(idOrShortId) }] },
|
|
||||||
{ organizationId: organizationId },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -101,9 +98,9 @@ const createVolume = async (name: string, backendConfig: BackendConfig) => {
|
||||||
return { volume: created, status: 201 };
|
return { volume: created, status: 201 };
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteVolume = async (idOrShortId: string | number) => {
|
const deleteVolume = async (identifier: string | number) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const volume = await findVolume(idOrShortId);
|
const volume = await findVolume(identifier);
|
||||||
|
|
||||||
if (!volume) {
|
if (!volume) {
|
||||||
throw new NotFoundError("Volume not found");
|
throw new NotFoundError("Volume not found");
|
||||||
|
|
@ -116,9 +113,9 @@ const deleteVolume = async (idOrShortId: string | number) => {
|
||||||
.where(and(eq(volumesTable.id, volume.id), eq(volumesTable.organizationId, organizationId)));
|
.where(and(eq(volumesTable.id, volume.id), eq(volumesTable.organizationId, organizationId)));
|
||||||
};
|
};
|
||||||
|
|
||||||
const mountVolume = async (idOrShortId: string | number) => {
|
const mountVolume = async (identifier: string | number) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const volume = await findVolume(idOrShortId);
|
const volume = await findVolume(identifier);
|
||||||
|
|
||||||
if (!volume) {
|
if (!volume) {
|
||||||
throw new NotFoundError("Volume not found");
|
throw new NotFoundError("Volume not found");
|
||||||
|
|
@ -139,9 +136,9 @@ const mountVolume = async (idOrShortId: string | number) => {
|
||||||
return { error, status };
|
return { error, status };
|
||||||
};
|
};
|
||||||
|
|
||||||
const unmountVolume = async (idOrShortId: string | number) => {
|
const unmountVolume = async (identifier: string | number) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const volume = await findVolume(idOrShortId);
|
const volume = await findVolume(identifier);
|
||||||
|
|
||||||
if (!volume) {
|
if (!volume) {
|
||||||
throw new NotFoundError("Volume not found");
|
throw new NotFoundError("Volume not found");
|
||||||
|
|
@ -162,8 +159,8 @@ const unmountVolume = async (idOrShortId: string | number) => {
|
||||||
return { error, status };
|
return { error, status };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getVolume = async (idOrShortId: string | number) => {
|
const getVolume = async (identifier: string | number) => {
|
||||||
const volume = await findVolume(idOrShortId);
|
const volume = await findVolume(identifier);
|
||||||
|
|
||||||
if (!volume) {
|
if (!volume) {
|
||||||
throw new NotFoundError("Volume not found");
|
throw new NotFoundError("Volume not found");
|
||||||
|
|
@ -180,9 +177,9 @@ const getVolume = async (idOrShortId: string | number) => {
|
||||||
return { volume, statfs };
|
return { volume, statfs };
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateVolume = async (idOrShortId: string | number, volumeData: UpdateVolumeBody) => {
|
const updateVolume = async (identifier: string | number, volumeData: UpdateVolumeBody) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const existing = await findVolume(idOrShortId);
|
const existing = await findVolume(identifier);
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
throw new NotFoundError("Volume not found");
|
throw new NotFoundError("Volume not found");
|
||||||
|
|
@ -273,9 +270,9 @@ const testConnection = async (backendConfig: BackendConfig) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkHealth = async (idOrShortId: string | number) => {
|
const checkHealth = async (identifier: string | number) => {
|
||||||
const organizationId = getOrganizationId();
|
const organizationId = getOrganizationId();
|
||||||
const volume = await findVolume(idOrShortId);
|
const volume = await findVolume(identifier);
|
||||||
|
|
||||||
if (!volume) {
|
if (!volume) {
|
||||||
throw new NotFoundError("Volume not found");
|
throw new NotFoundError("Volume not found");
|
||||||
|
|
@ -300,12 +297,12 @@ const DEFAULT_PAGE_SIZE = 500;
|
||||||
const MAX_PAGE_SIZE = 500;
|
const MAX_PAGE_SIZE = 500;
|
||||||
|
|
||||||
const listFiles = async (
|
const listFiles = async (
|
||||||
idOrShortId: string | number,
|
identifier: string | number,
|
||||||
subPath?: string,
|
subPath?: string,
|
||||||
offset: number = 0,
|
offset: number = 0,
|
||||||
limit: number = DEFAULT_PAGE_SIZE,
|
limit: number = DEFAULT_PAGE_SIZE,
|
||||||
) => {
|
) => {
|
||||||
const volume = await findVolume(idOrShortId);
|
const volume = await findVolume(identifier);
|
||||||
|
|
||||||
if (!volume) {
|
if (!volume) {
|
||||||
throw new NotFoundError("Volume not found");
|
throw new NotFoundError("Volume not found");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue