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