From 54ee02deb92be135a7dbccadbce526d8849fe7d1 Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Sat, 15 Nov 2025 11:24:13 +0100 Subject: [PATCH] feat(backups): manual repository cleanup --- .../api-client/@tanstack/react-query.gen.ts | 1265 ++++---- app/client/api-client/client.gen.ts | 16 +- app/client/api-client/client/client.gen.ts | 489 +-- app/client/api-client/client/index.ts | 42 +- app/client/api-client/client/types.gen.ts | 331 +- app/client/api-client/client/utils.gen.ts | 481 +-- app/client/api-client/core/auth.gen.ts | 55 +- .../api-client/core/bodySerializer.gen.ts | 124 +- app/client/api-client/core/params.gen.ts | 269 +- .../api-client/core/pathSerializer.gen.ts | 266 +- .../api-client/core/queryKeySerializer.gen.ts | 157 +- .../api-client/core/serverSentEvents.gen.ts | 421 +-- app/client/api-client/core/types.gen.ts | 178 +- app/client/api-client/core/utils.gen.ts | 214 +- app/client/api-client/index.ts | 4 +- app/client/api-client/sdk.gen.ts | 633 ++-- app/client/api-client/types.gen.ts | 2705 ++++++++--------- .../backups/components/schedule-summary.tsx | 50 +- .../modules/backups/backups.controller.ts | 9 + app/server/modules/backups/backups.dto.ts | 25 + app/server/modules/backups/backups.service.ts | 27 + 21 files changed, 3862 insertions(+), 3899 deletions(-) diff --git a/app/client/api-client/@tanstack/react-query.gen.ts b/app/client/api-client/@tanstack/react-query.gen.ts index 8767f0f..910e0fd 100644 --- a/app/client/api-client/@tanstack/react-query.gen.ts +++ b/app/client/api-client/@tanstack/react-query.gen.ts @@ -1,222 +1,95 @@ // This file is auto-generated by @hey-api/openapi-ts -import { type DefaultError, queryOptions, type UseMutationOptions } from "@tanstack/react-query"; +import { type DefaultError, queryOptions, type UseMutationOptions } from '@tanstack/react-query'; -import { client } from "../client.gen"; -import { - browseFilesystem, - changePassword, - createBackupSchedule, - createRepository, - createVolume, - deleteBackupSchedule, - deleteRepository, - deleteVolume, - doctorRepository, - downloadResticPassword, - getBackupSchedule, - getBackupScheduleForVolume, - getContainersUsingVolume, - getMe, - getRepository, - getSnapshotDetails, - getStatus, - getSystemInfo, - getVolume, - healthCheckVolume, - listBackupSchedules, - listFiles, - listRcloneRemotes, - listRepositories, - listSnapshotFiles, - listSnapshots, - listVolumes, - login, - logout, - mountVolume, - type Options, - register, - restoreSnapshot, - runBackupNow, - stopBackup, - testConnection, - unmountVolume, - updateBackupSchedule, - updateVolume, -} from "../sdk.gen"; -import type { - BrowseFilesystemData, - BrowseFilesystemResponse, - ChangePasswordData, - ChangePasswordResponse, - CreateBackupScheduleData, - CreateBackupScheduleResponse, - CreateRepositoryData, - CreateRepositoryResponse, - CreateVolumeData, - CreateVolumeResponse, - DeleteBackupScheduleData, - DeleteBackupScheduleResponse, - DeleteRepositoryData, - DeleteRepositoryResponse, - DeleteVolumeData, - DeleteVolumeResponse, - DoctorRepositoryData, - DoctorRepositoryResponse, - DownloadResticPasswordData, - DownloadResticPasswordResponse, - GetBackupScheduleData, - GetBackupScheduleForVolumeData, - GetBackupScheduleForVolumeResponse, - GetBackupScheduleResponse, - GetContainersUsingVolumeData, - GetContainersUsingVolumeResponse, - GetMeData, - GetMeResponse, - GetRepositoryData, - GetRepositoryResponse, - GetSnapshotDetailsData, - GetSnapshotDetailsResponse, - GetStatusData, - GetStatusResponse, - GetSystemInfoData, - GetSystemInfoResponse, - GetVolumeData, - GetVolumeResponse, - HealthCheckVolumeData, - HealthCheckVolumeResponse, - ListBackupSchedulesData, - ListBackupSchedulesResponse, - ListFilesData, - ListFilesResponse, - ListRcloneRemotesData, - ListRcloneRemotesResponse, - ListRepositoriesData, - ListRepositoriesResponse, - ListSnapshotFilesData, - ListSnapshotFilesResponse, - ListSnapshotsData, - ListSnapshotsResponse, - ListVolumesData, - ListVolumesResponse, - LoginData, - LoginResponse, - LogoutData, - LogoutResponse, - MountVolumeData, - MountVolumeResponse, - RegisterData, - RegisterResponse, - RestoreSnapshotData, - RestoreSnapshotResponse, - RunBackupNowData, - RunBackupNowResponse, - StopBackupData, - StopBackupResponse, - TestConnectionData, - TestConnectionResponse, - UnmountVolumeData, - UnmountVolumeResponse, - UpdateBackupScheduleData, - UpdateBackupScheduleResponse, - UpdateVolumeData, - UpdateVolumeResponse, -} from "../types.gen"; +import { client } from '../client.gen'; +import { browseFilesystem, changePassword, createBackupSchedule, createRepository, createVolume, deleteBackupSchedule, deleteRepository, deleteVolume, doctorRepository, downloadResticPassword, getBackupSchedule, getBackupScheduleForVolume, getContainersUsingVolume, getMe, getRepository, getSnapshotDetails, getStatus, getSystemInfo, getVolume, healthCheckVolume, listBackupSchedules, listFiles, listRcloneRemotes, listRepositories, listSnapshotFiles, listSnapshots, listVolumes, login, logout, mountVolume, type Options, register, restoreSnapshot, runBackupNow, runForget, stopBackup, testConnection, unmountVolume, updateBackupSchedule, updateVolume } from '../sdk.gen'; +import type { BrowseFilesystemData, BrowseFilesystemResponse, ChangePasswordData, ChangePasswordResponse, CreateBackupScheduleData, CreateBackupScheduleResponse, CreateRepositoryData, CreateRepositoryResponse, CreateVolumeData, CreateVolumeResponse, DeleteBackupScheduleData, DeleteBackupScheduleResponse, DeleteRepositoryData, DeleteRepositoryResponse, DeleteVolumeData, DeleteVolumeResponse, DoctorRepositoryData, DoctorRepositoryResponse, DownloadResticPasswordData, DownloadResticPasswordResponse, GetBackupScheduleData, GetBackupScheduleForVolumeData, GetBackupScheduleForVolumeResponse, GetBackupScheduleResponse, GetContainersUsingVolumeData, GetContainersUsingVolumeResponse, GetMeData, GetMeResponse, GetRepositoryData, GetRepositoryResponse, GetSnapshotDetailsData, GetSnapshotDetailsResponse, GetStatusData, GetStatusResponse, GetSystemInfoData, GetSystemInfoResponse, GetVolumeData, GetVolumeResponse, HealthCheckVolumeData, HealthCheckVolumeResponse, ListBackupSchedulesData, ListBackupSchedulesResponse, ListFilesData, ListFilesResponse, ListRcloneRemotesData, ListRcloneRemotesResponse, ListRepositoriesData, ListRepositoriesResponse, ListSnapshotFilesData, ListSnapshotFilesResponse, ListSnapshotsData, ListSnapshotsResponse, ListVolumesData, ListVolumesResponse, LoginData, LoginResponse, LogoutData, LogoutResponse, MountVolumeData, MountVolumeResponse, RegisterData, RegisterResponse, RestoreSnapshotData, RestoreSnapshotResponse, RunBackupNowData, RunBackupNowResponse, RunForgetData, RunForgetResponse, StopBackupData, StopBackupResponse, TestConnectionData, TestConnectionResponse, UnmountVolumeData, UnmountVolumeResponse, UpdateBackupScheduleData, UpdateBackupScheduleResponse, UpdateVolumeData, UpdateVolumeResponse } from '../types.gen'; /** * Register a new user */ -export const registerMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await register({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const registerMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await register({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; /** * Login with username and password */ -export const loginMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await login({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const loginMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await login({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; /** * Logout current user */ -export const logoutMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await logout({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const logoutMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await logout({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; export type QueryKey = [ - Pick & { - _id: string; - _infinite?: boolean; - tags?: ReadonlyArray; - }, + Pick & { + _id: string; + _infinite?: boolean; + tags?: ReadonlyArray; + } ]; -const createQueryKey = ( - id: string, - options?: TOptions, - infinite?: boolean, - tags?: ReadonlyArray, -): [QueryKey[0]] => { - const params: QueryKey[0] = { - _id: id, - baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl, - } as QueryKey[0]; - if (infinite) { - params._infinite = infinite; - } - if (tags) { - params.tags = tags; - } - if (options?.body) { - params.body = options.body; - } - if (options?.headers) { - params.headers = options.headers; - } - if (options?.path) { - params.path = options.path; - } - if (options?.query) { - params.query = options.query; - } - return [params]; +const createQueryKey = (id: string, options?: TOptions, infinite?: boolean, tags?: ReadonlyArray): [ + QueryKey[0] +] => { + const params: QueryKey[0] = { _id: id, baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl } as QueryKey[0]; + if (infinite) { + params._infinite = infinite; + } + if (tags) { + params.tags = tags; + } + if (options?.body) { + params.body = options.body; + } + if (options?.headers) { + params.headers = options.headers; + } + if (options?.path) { + params.path = options.path; + } + if (options?.query) { + params.query = options.query; + } + return [ + params + ]; }; export const getMeQueryKey = (options?: Options) => createQueryKey("getMe", options); @@ -224,56 +97,52 @@ export const getMeQueryKey = (options?: Options) => createQueryKey("g /** * Get current authenticated user */ -export const getMeOptions = (options?: Options) => - queryOptions>({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await getMe({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: getMeQueryKey(options), - }); +export const getMeOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getMe({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getMeQueryKey(options) +}); export const getStatusQueryKey = (options?: Options) => createQueryKey("getStatus", options); /** * Get authentication system status */ -export const getStatusOptions = (options?: Options) => - queryOptions>({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await getStatus({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: getStatusQueryKey(options), - }); +export const getStatusOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getStatus({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getStatusQueryKey(options) +}); /** * Change current user password */ -export const changePasswordMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await changePassword({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const changePasswordMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await changePassword({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; export const listVolumesQueryKey = (options?: Options) => createQueryKey("listVolumes", options); @@ -281,75 +150,68 @@ export const listVolumesQueryKey = (options?: Options) => creat /** * List all volumes */ -export const listVolumesOptions = (options?: Options) => - queryOptions>({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await listVolumes({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: listVolumesQueryKey(options), - }); +export const listVolumesOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await listVolumes({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: listVolumesQueryKey(options) +}); /** * Create a new volume */ -export const createVolumeMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await createVolume({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const createVolumeMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await createVolume({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; /** * Test connection to backend */ -export const testConnectionMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await testConnection({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const testConnectionMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await testConnection({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; /** * Delete a volume */ -export const deleteVolumeMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await deleteVolume({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const deleteVolumeMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await deleteVolume({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; export const getVolumeQueryKey = (options: Options) => createQueryKey("getVolume", options); @@ -357,119 +219,103 @@ export const getVolumeQueryKey = (options: Options) => createQuer /** * Get a volume by name */ -export const getVolumeOptions = (options: Options) => - queryOptions>({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await getVolume({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: getVolumeQueryKey(options), - }); +export const getVolumeOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getVolume({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getVolumeQueryKey(options) +}); /** * Update a volume's configuration */ -export const updateVolumeMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await updateVolume({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const updateVolumeMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await updateVolume({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; -export const getContainersUsingVolumeQueryKey = (options: Options) => - createQueryKey("getContainersUsingVolume", options); +export const getContainersUsingVolumeQueryKey = (options: Options) => createQueryKey("getContainersUsingVolume", options); /** * Get containers using a volume by name */ -export const getContainersUsingVolumeOptions = (options: Options) => - queryOptions< - GetContainersUsingVolumeResponse, - DefaultError, - GetContainersUsingVolumeResponse, - ReturnType - >({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await getContainersUsingVolume({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: getContainersUsingVolumeQueryKey(options), - }); +export const getContainersUsingVolumeOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getContainersUsingVolume({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getContainersUsingVolumeQueryKey(options) +}); /** * Mount a volume */ -export const mountVolumeMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await mountVolume({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const mountVolumeMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await mountVolume({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; /** * Unmount a volume */ -export const unmountVolumeMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await unmountVolume({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const unmountVolumeMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await unmountVolume({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; /** * Perform a health check on a volume */ -export const healthCheckVolumeMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await healthCheckVolume({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const healthCheckVolumeMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await healthCheckVolume({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; export const listFilesQueryKey = (options: Options) => createQueryKey("listFiles", options); @@ -477,131 +323,105 @@ export const listFilesQueryKey = (options: Options) => createQuer /** * List files in a volume directory */ -export const listFilesOptions = (options: Options) => - queryOptions>({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await listFiles({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: listFilesQueryKey(options), - }); +export const listFilesOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await listFiles({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: listFilesQueryKey(options) +}); -export const browseFilesystemQueryKey = (options?: Options) => - createQueryKey("browseFilesystem", options); +export const browseFilesystemQueryKey = (options?: Options) => createQueryKey("browseFilesystem", options); /** * Browse directories on the host filesystem */ -export const browseFilesystemOptions = (options?: Options) => - queryOptions< - BrowseFilesystemResponse, - DefaultError, - BrowseFilesystemResponse, - ReturnType - >({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await browseFilesystem({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: browseFilesystemQueryKey(options), - }); +export const browseFilesystemOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await browseFilesystem({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: browseFilesystemQueryKey(options) +}); -export const listRepositoriesQueryKey = (options?: Options) => - createQueryKey("listRepositories", options); +export const listRepositoriesQueryKey = (options?: Options) => createQueryKey("listRepositories", options); /** * List all repositories */ -export const listRepositoriesOptions = (options?: Options) => - queryOptions< - ListRepositoriesResponse, - DefaultError, - ListRepositoriesResponse, - ReturnType - >({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await listRepositories({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: listRepositoriesQueryKey(options), - }); +export const listRepositoriesOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await listRepositories({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: listRepositoriesQueryKey(options) +}); /** * Create a new restic repository */ -export const createRepositoryMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await createRepository({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const createRepositoryMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await createRepository({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; -export const listRcloneRemotesQueryKey = (options?: Options) => - createQueryKey("listRcloneRemotes", options); +export const listRcloneRemotesQueryKey = (options?: Options) => createQueryKey("listRcloneRemotes", options); /** * List all configured rclone remotes on the host system */ -export const listRcloneRemotesOptions = (options?: Options) => - queryOptions< - ListRcloneRemotesResponse, - DefaultError, - ListRcloneRemotesResponse, - ReturnType - >({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await listRcloneRemotes({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: listRcloneRemotesQueryKey(options), - }); +export const listRcloneRemotesOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await listRcloneRemotes({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: listRcloneRemotesQueryKey(options) +}); /** * Delete a repository */ -export const deleteRepositoryMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await deleteRepository({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const deleteRepositoryMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await deleteRepository({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; export const getRepositoryQueryKey = (options: Options) => createQueryKey("getRepository", options); @@ -609,307 +429,261 @@ export const getRepositoryQueryKey = (options: Options) => cr /** * Get a single repository by name */ -export const getRepositoryOptions = (options: Options) => - queryOptions>({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await getRepository({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: getRepositoryQueryKey(options), - }); +export const getRepositoryOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getRepository({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getRepositoryQueryKey(options) +}); export const listSnapshotsQueryKey = (options: Options) => createQueryKey("listSnapshots", options); /** * List all snapshots in a repository */ -export const listSnapshotsOptions = (options: Options) => - queryOptions>({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await listSnapshots({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: listSnapshotsQueryKey(options), - }); +export const listSnapshotsOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await listSnapshots({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: listSnapshotsQueryKey(options) +}); -export const getSnapshotDetailsQueryKey = (options: Options) => - createQueryKey("getSnapshotDetails", options); +export const getSnapshotDetailsQueryKey = (options: Options) => createQueryKey("getSnapshotDetails", options); /** * Get details of a specific snapshot */ -export const getSnapshotDetailsOptions = (options: Options) => - queryOptions< - GetSnapshotDetailsResponse, - DefaultError, - GetSnapshotDetailsResponse, - ReturnType - >({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await getSnapshotDetails({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: getSnapshotDetailsQueryKey(options), - }); +export const getSnapshotDetailsOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getSnapshotDetails({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getSnapshotDetailsQueryKey(options) +}); -export const listSnapshotFilesQueryKey = (options: Options) => - createQueryKey("listSnapshotFiles", options); +export const listSnapshotFilesQueryKey = (options: Options) => createQueryKey("listSnapshotFiles", options); /** * List files and directories in a snapshot */ -export const listSnapshotFilesOptions = (options: Options) => - queryOptions< - ListSnapshotFilesResponse, - DefaultError, - ListSnapshotFilesResponse, - ReturnType - >({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await listSnapshotFiles({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: listSnapshotFilesQueryKey(options), - }); +export const listSnapshotFilesOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await listSnapshotFiles({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: listSnapshotFilesQueryKey(options) +}); /** * Restore a snapshot to a target path on the filesystem */ -export const restoreSnapshotMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await restoreSnapshot({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const restoreSnapshotMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await restoreSnapshot({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; /** * Run doctor operations on a repository to fix common issues (unlock, check, repair index). Use this when the repository is locked or has errors. */ -export const doctorRepositoryMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await doctorRepository({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const doctorRepositoryMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await doctorRepository({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; -export const listBackupSchedulesQueryKey = (options?: Options) => - createQueryKey("listBackupSchedules", options); +export const listBackupSchedulesQueryKey = (options?: Options) => createQueryKey("listBackupSchedules", options); /** * List all backup schedules */ -export const listBackupSchedulesOptions = (options?: Options) => - queryOptions< - ListBackupSchedulesResponse, - DefaultError, - ListBackupSchedulesResponse, - ReturnType - >({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await listBackupSchedules({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: listBackupSchedulesQueryKey(options), - }); +export const listBackupSchedulesOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await listBackupSchedules({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: listBackupSchedulesQueryKey(options) +}); /** * Create a new backup schedule for a volume */ -export const createBackupScheduleMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions< - CreateBackupScheduleResponse, - DefaultError, - Options - > = { - mutationFn: async (fnOptions) => { - const { data } = await createBackupSchedule({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const createBackupScheduleMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await createBackupSchedule({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; /** * Delete a backup schedule */ -export const deleteBackupScheduleMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions< - DeleteBackupScheduleResponse, - DefaultError, - Options - > = { - mutationFn: async (fnOptions) => { - const { data } = await deleteBackupSchedule({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const deleteBackupScheduleMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await deleteBackupSchedule({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; -export const getBackupScheduleQueryKey = (options: Options) => - createQueryKey("getBackupSchedule", options); +export const getBackupScheduleQueryKey = (options: Options) => createQueryKey("getBackupSchedule", options); /** * Get a backup schedule by ID */ -export const getBackupScheduleOptions = (options: Options) => - queryOptions< - GetBackupScheduleResponse, - DefaultError, - GetBackupScheduleResponse, - ReturnType - >({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await getBackupSchedule({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: getBackupScheduleQueryKey(options), - }); +export const getBackupScheduleOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getBackupSchedule({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getBackupScheduleQueryKey(options) +}); /** * Update a backup schedule */ -export const updateBackupScheduleMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions< - UpdateBackupScheduleResponse, - DefaultError, - Options - > = { - mutationFn: async (fnOptions) => { - const { data } = await updateBackupSchedule({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const updateBackupScheduleMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await updateBackupSchedule({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; -export const getBackupScheduleForVolumeQueryKey = (options: Options) => - createQueryKey("getBackupScheduleForVolume", options); +export const getBackupScheduleForVolumeQueryKey = (options: Options) => createQueryKey("getBackupScheduleForVolume", options); /** * Get a backup schedule for a specific volume */ -export const getBackupScheduleForVolumeOptions = (options: Options) => - queryOptions< - GetBackupScheduleForVolumeResponse, - DefaultError, - GetBackupScheduleForVolumeResponse, - ReturnType - >({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await getBackupScheduleForVolume({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: getBackupScheduleForVolumeQueryKey(options), - }); +export const getBackupScheduleForVolumeOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getBackupScheduleForVolume({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getBackupScheduleForVolumeQueryKey(options) +}); /** * Trigger a backup immediately for a schedule */ -export const runBackupNowMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await runBackupNow({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const runBackupNowMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await runBackupNow({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; /** * Stop a backup that is currently in progress */ -export const stopBackupMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (fnOptions) => { - const { data } = await stopBackup({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const stopBackupMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await stopBackup({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; +}; + +/** + * Manually apply retention policy to clean up old snapshots + */ +export const runForgetMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await runForget({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; export const getSystemInfoQueryKey = (options?: Options) => createQueryKey("getSystemInfo", options); @@ -917,39 +691,32 @@ export const getSystemInfoQueryKey = (options?: Options) => c /** * Get system information including available capabilities */ -export const getSystemInfoOptions = (options?: Options) => - queryOptions>({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await getSystemInfo({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: getSystemInfoQueryKey(options), - }); +export const getSystemInfoOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getSystemInfo({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getSystemInfoQueryKey(options) +}); /** * Download the Restic password file for backup recovery. Requires password re-authentication. */ -export const downloadResticPasswordMutation = ( - options?: Partial>, -): UseMutationOptions> => { - const mutationOptions: UseMutationOptions< - DownloadResticPasswordResponse, - DefaultError, - Options - > = { - mutationFn: async (fnOptions) => { - const { data } = await downloadResticPassword({ - ...options, - ...fnOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; +export const downloadResticPasswordMutation = (options?: Partial>): UseMutationOptions> => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (fnOptions) => { + const { data } = await downloadResticPassword({ + ...options, + ...fnOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; }; diff --git a/app/client/api-client/client.gen.ts b/app/client/api-client/client.gen.ts index 2695317..50c3a3f 100644 --- a/app/client/api-client/client.gen.ts +++ b/app/client/api-client/client.gen.ts @@ -1,7 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts -import { type ClientOptions, type Config, createClient, createConfig } from "./client"; -import type { ClientOptions as ClientOptions2 } from "./types.gen"; +import { type ClientOptions, type Config, createClient, createConfig } from './client'; +import type { ClientOptions as ClientOptions2 } from './types.gen'; /** * The `createClientConfig()` function will be called on client initialization @@ -11,12 +11,8 @@ import type { ClientOptions as ClientOptions2 } from "./types.gen"; * `setConfig()`. This is useful for example if you're using Next.js * to ensure your client always has the correct values. */ -export type CreateClientConfig = ( - override?: Config, -) => Config & T>; +export type CreateClientConfig = (override?: Config) => Config & T>; -export const client = createClient( - createConfig({ - baseUrl: "http://192.168.2.42:4096", - }), -); +export const client = createClient(createConfig({ + baseUrl: 'http://192.168.2.42:4096' +})); diff --git a/app/client/api-client/client/client.gen.ts b/app/client/api-client/client/client.gen.ts index e30d8ad..c2a5190 100644 --- a/app/client/api-client/client/client.gen.ts +++ b/app/client/api-client/client/client.gen.ts @@ -1,278 +1,301 @@ // This file is auto-generated by @hey-api/openapi-ts -import { createSseClient } from "../core/serverSentEvents.gen"; -import type { HttpMethod } from "../core/types.gen"; -import { getValidRequestBody } from "../core/utils.gen"; -import type { Client, Config, RequestOptions, ResolvedRequestOptions } from "./types.gen"; +import { createSseClient } from '../core/serverSentEvents.gen'; +import type { HttpMethod } from '../core/types.gen'; +import { getValidRequestBody } from '../core/utils.gen'; +import type { + Client, + Config, + RequestOptions, + ResolvedRequestOptions, +} from './types.gen'; import { - buildUrl, - createConfig, - createInterceptors, - getParseAs, - mergeConfigs, - mergeHeaders, - setAuthParams, -} from "./utils.gen"; + buildUrl, + createConfig, + createInterceptors, + getParseAs, + mergeConfigs, + mergeHeaders, + setAuthParams, +} from './utils.gen'; -type ReqInit = Omit & { - body?: any; - headers: ReturnType; +type ReqInit = Omit & { + body?: any; + headers: ReturnType; }; export const createClient = (config: Config = {}): Client => { - let _config = mergeConfigs(createConfig(), config); + let _config = mergeConfigs(createConfig(), config); - const getConfig = (): Config => ({ ..._config }); + const getConfig = (): Config => ({ ..._config }); - const setConfig = (config: Config): Config => { - _config = mergeConfigs(_config, config); - return getConfig(); - }; + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config); + return getConfig(); + }; - const interceptors = createInterceptors(); + const interceptors = createInterceptors< + Request, + Response, + unknown, + ResolvedRequestOptions + >(); - const beforeRequest = async (options: RequestOptions) => { - const opts = { - ..._config, - ...options, - fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, - headers: mergeHeaders(_config.headers, options.headers), - serializedBody: undefined, - }; + const beforeRequest = async (options: RequestOptions) => { + const opts = { + ..._config, + ...options, + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, + headers: mergeHeaders(_config.headers, options.headers), + serializedBody: undefined, + }; - if (opts.security) { - await setAuthParams({ - ...opts, - security: opts.security, - }); - } + if (opts.security) { + await setAuthParams({ + ...opts, + security: opts.security, + }); + } - if (opts.requestValidator) { - await opts.requestValidator(opts); - } + if (opts.requestValidator) { + await opts.requestValidator(opts); + } - if (opts.body !== undefined && opts.bodySerializer) { - opts.serializedBody = opts.bodySerializer(opts.body); - } + if (opts.body !== undefined && opts.bodySerializer) { + opts.serializedBody = opts.bodySerializer(opts.body); + } - // remove Content-Type header if body is empty to avoid sending invalid requests - if (opts.body === undefined || opts.serializedBody === "") { - opts.headers.delete("Content-Type"); - } + // remove Content-Type header if body is empty to avoid sending invalid requests + if (opts.body === undefined || opts.serializedBody === '') { + opts.headers.delete('Content-Type'); + } - const url = buildUrl(opts); + const url = buildUrl(opts); - return { opts, url }; - }; + return { opts, url }; + }; - const request: Client["request"] = async (options) => { - // @ts-expect-error - const { opts, url } = await beforeRequest(options); - const requestInit: ReqInit = { - redirect: "follow", - ...opts, - body: getValidRequestBody(opts), - }; + const request: Client['request'] = async (options) => { + // @ts-expect-error + const { opts, url } = await beforeRequest(options); + const requestInit: ReqInit = { + redirect: 'follow', + ...opts, + body: getValidRequestBody(opts), + }; - let request = new Request(url, requestInit); + let request = new Request(url, requestInit); - for (const fn of interceptors.request.fns) { - if (fn) { - request = await fn(request, opts); - } - } + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } - // fetch must be assigned here, otherwise it would throw the error: - // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation - const _fetch = opts.fetch!; - let response: Response; + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = opts.fetch!; + let response: Response; - try { - response = await _fetch(request); - } catch (error) { - // Handle fetch exceptions (AbortError, network errors, etc.) - let finalError = error; + try { + response = await _fetch(request); + } catch (error) { + // Handle fetch exceptions (AbortError, network errors, etc.) + let finalError = error; - for (const fn of interceptors.error.fns) { - if (fn) { - finalError = (await fn(error, undefined as any, request, opts)) as unknown; - } - } + for (const fn of interceptors.error.fns) { + if (fn) { + finalError = (await fn( + error, + undefined as any, + request, + opts, + )) as unknown; + } + } - finalError = finalError || ({} as unknown); + finalError = finalError || ({} as unknown); - if (opts.throwOnError) { - throw finalError; - } + if (opts.throwOnError) { + throw finalError; + } - // Return error response - return opts.responseStyle === "data" - ? undefined - : { - error: finalError, - request, - response: undefined as any, - }; - } + // Return error response + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + request, + response: undefined as any, + }; + } - for (const fn of interceptors.response.fns) { - if (fn) { - response = await fn(response, request, opts); - } - } + for (const fn of interceptors.response.fns) { + if (fn) { + response = await fn(response, request, opts); + } + } - const result = { - request, - response, - }; + const result = { + request, + response, + }; - if (response.ok) { - const parseAs = - (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json"; + if (response.ok) { + const parseAs = + (opts.parseAs === 'auto' + ? getParseAs(response.headers.get('Content-Type')) + : opts.parseAs) ?? 'json'; - if (response.status === 204 || response.headers.get("Content-Length") === "0") { - let emptyData: any; - switch (parseAs) { - case "arrayBuffer": - case "blob": - case "text": - emptyData = await response[parseAs](); - break; - case "formData": - emptyData = new FormData(); - break; - case "stream": - emptyData = response.body; - break; - case "json": - default: - emptyData = {}; - break; - } - return opts.responseStyle === "data" - ? emptyData - : { - data: emptyData, - ...result, - }; - } + if ( + response.status === 204 || + response.headers.get('Content-Length') === '0' + ) { + let emptyData: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'text': + emptyData = await response[parseAs](); + break; + case 'formData': + emptyData = new FormData(); + break; + case 'stream': + emptyData = response.body; + break; + case 'json': + default: + emptyData = {}; + break; + } + return opts.responseStyle === 'data' + ? emptyData + : { + data: emptyData, + ...result, + }; + } - let data: any; - switch (parseAs) { - case "arrayBuffer": - case "blob": - case "formData": - case "json": - case "text": - data = await response[parseAs](); - break; - case "stream": - return opts.responseStyle === "data" - ? response.body - : { - data: response.body, - ...result, - }; - } + let data: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'formData': + case 'json': + case 'text': + data = await response[parseAs](); + break; + case 'stream': + return opts.responseStyle === 'data' + ? response.body + : { + data: response.body, + ...result, + }; + } - if (parseAs === "json") { - if (opts.responseValidator) { - await opts.responseValidator(data); - } + if (parseAs === 'json') { + if (opts.responseValidator) { + await opts.responseValidator(data); + } - if (opts.responseTransformer) { - data = await opts.responseTransformer(data); - } - } + if (opts.responseTransformer) { + data = await opts.responseTransformer(data); + } + } - return opts.responseStyle === "data" - ? data - : { - data, - ...result, - }; - } + return opts.responseStyle === 'data' + ? data + : { + data, + ...result, + }; + } - const textError = await response.text(); - let jsonError: unknown; + const textError = await response.text(); + let jsonError: unknown; - try { - jsonError = JSON.parse(textError); - } catch { - // noop - } + try { + jsonError = JSON.parse(textError); + } catch { + // noop + } - const error = jsonError ?? textError; - let finalError = error; + const error = jsonError ?? textError; + let finalError = error; - for (const fn of interceptors.error.fns) { - if (fn) { - finalError = (await fn(error, response, request, opts)) as string; - } - } + for (const fn of interceptors.error.fns) { + if (fn) { + finalError = (await fn(error, response, request, opts)) as string; + } + } - finalError = finalError || ({} as string); + finalError = finalError || ({} as string); - if (opts.throwOnError) { - throw finalError; - } + if (opts.throwOnError) { + throw finalError; + } - // TODO: we probably want to return error and improve types - return opts.responseStyle === "data" - ? undefined - : { - error: finalError, - ...result, - }; - }; + // TODO: we probably want to return error and improve types + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + ...result, + }; + }; - const makeMethodFn = (method: Uppercase) => (options: RequestOptions) => request({ ...options, method }); + const makeMethodFn = + (method: Uppercase) => (options: RequestOptions) => + request({ ...options, method }); - const makeSseFn = (method: Uppercase) => async (options: RequestOptions) => { - const { opts, url } = await beforeRequest(options); - return createSseClient({ - ...opts, - body: opts.body as BodyInit | null | undefined, - headers: opts.headers as unknown as Record, - method, - onRequest: async (url, init) => { - let request = new Request(url, init); - for (const fn of interceptors.request.fns) { - if (fn) { - request = await fn(request, opts); - } - } - return request; - }, - url, - }); - }; + const makeSseFn = + (method: Uppercase) => async (options: RequestOptions) => { + const { opts, url } = await beforeRequest(options); + return createSseClient({ + ...opts, + body: opts.body as BodyInit | null | undefined, + headers: opts.headers as unknown as Record, + method, + onRequest: async (url, init) => { + let request = new Request(url, init); + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + return request; + }, + url, + }); + }; - return { - buildUrl, - connect: makeMethodFn("CONNECT"), - delete: makeMethodFn("DELETE"), - get: makeMethodFn("GET"), - getConfig, - head: makeMethodFn("HEAD"), - interceptors, - options: makeMethodFn("OPTIONS"), - patch: makeMethodFn("PATCH"), - post: makeMethodFn("POST"), - put: makeMethodFn("PUT"), - request, - setConfig, - sse: { - connect: makeSseFn("CONNECT"), - delete: makeSseFn("DELETE"), - get: makeSseFn("GET"), - head: makeSseFn("HEAD"), - options: makeSseFn("OPTIONS"), - patch: makeSseFn("PATCH"), - post: makeSseFn("POST"), - put: makeSseFn("PUT"), - trace: makeSseFn("TRACE"), - }, - trace: makeMethodFn("TRACE"), - } as Client; + return { + buildUrl, + connect: makeMethodFn('CONNECT'), + delete: makeMethodFn('DELETE'), + get: makeMethodFn('GET'), + getConfig, + head: makeMethodFn('HEAD'), + interceptors, + options: makeMethodFn('OPTIONS'), + patch: makeMethodFn('PATCH'), + post: makeMethodFn('POST'), + put: makeMethodFn('PUT'), + request, + setConfig, + sse: { + connect: makeSseFn('CONNECT'), + delete: makeSseFn('DELETE'), + get: makeSseFn('GET'), + head: makeSseFn('HEAD'), + options: makeSseFn('OPTIONS'), + patch: makeSseFn('PATCH'), + post: makeSseFn('POST'), + put: makeSseFn('PUT'), + trace: makeSseFn('TRACE'), + }, + trace: makeMethodFn('TRACE'), + } as Client; }; diff --git a/app/client/api-client/client/index.ts b/app/client/api-client/client/index.ts index 99ab72d..b295ede 100644 --- a/app/client/api-client/client/index.ts +++ b/app/client/api-client/client/index.ts @@ -1,25 +1,25 @@ // This file is auto-generated by @hey-api/openapi-ts -export type { Auth } from "../core/auth.gen"; -export type { QuerySerializerOptions } from "../core/bodySerializer.gen"; +export type { Auth } from '../core/auth.gen'; +export type { QuerySerializerOptions } from '../core/bodySerializer.gen'; export { - formDataBodySerializer, - jsonBodySerializer, - urlSearchParamsBodySerializer, -} from "../core/bodySerializer.gen"; -export { buildClientParams } from "../core/params.gen"; -export { serializeQueryKeyValue } from "../core/queryKeySerializer.gen"; -export { createClient } from "./client.gen"; + formDataBodySerializer, + jsonBodySerializer, + urlSearchParamsBodySerializer, +} from '../core/bodySerializer.gen'; +export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; +export { createClient } from './client.gen'; export type { - Client, - ClientOptions, - Config, - CreateClientConfig, - Options, - RequestOptions, - RequestResult, - ResolvedRequestOptions, - ResponseStyle, - TDataShape, -} from "./types.gen"; -export { createConfig, mergeHeaders } from "./utils.gen"; + Client, + ClientOptions, + Config, + CreateClientConfig, + Options, + RequestOptions, + RequestResult, + ResolvedRequestOptions, + ResponseStyle, + TDataShape, +} from './types.gen'; +export { createConfig, mergeHeaders } from './utils.gen'; diff --git a/app/client/api-client/client/types.gen.ts b/app/client/api-client/client/types.gen.ts index 71ed2eb..b4a499c 100644 --- a/app/client/api-client/client/types.gen.ts +++ b/app/client/api-client/client/types.gen.ts @@ -1,174 +1,210 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Auth } from "../core/auth.gen"; -import type { ServerSentEventsOptions, ServerSentEventsResult } from "../core/serverSentEvents.gen"; -import type { Client as CoreClient, Config as CoreConfig } from "../core/types.gen"; -import type { Middleware } from "./utils.gen"; +import type { Auth } from '../core/auth.gen'; +import type { + ServerSentEventsOptions, + ServerSentEventsResult, +} from '../core/serverSentEvents.gen'; +import type { + Client as CoreClient, + Config as CoreConfig, +} from '../core/types.gen'; +import type { Middleware } from './utils.gen'; -export type ResponseStyle = "data" | "fields"; +export type ResponseStyle = 'data' | 'fields'; export interface Config - extends Omit, - CoreConfig { - /** - * Base URL for all requests made by this client. - */ - baseUrl?: T["baseUrl"]; - /** - * Fetch API implementation. You can use this option to provide a custom - * fetch instance. - * - * @default globalThis.fetch - */ - fetch?: typeof fetch; - /** - * Please don't use the Fetch client for Next.js applications. The `next` - * options won't have any effect. - * - * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. - */ - next?: never; - /** - * Return the response data parsed in a specified format. By default, `auto` - * will infer the appropriate method from the `Content-Type` response header. - * You can override this behavior with any of the {@link Body} methods. - * Select `stream` if you don't want to parse response data at all. - * - * @default 'auto' - */ - parseAs?: "arrayBuffer" | "auto" | "blob" | "formData" | "json" | "stream" | "text"; - /** - * Should we return only data or multiple fields (data, error, response, etc.)? - * - * @default 'fields' - */ - responseStyle?: ResponseStyle; - /** - * Throw an error instead of returning it in the response? - * - * @default false - */ - throwOnError?: T["throwOnError"]; + extends Omit, + CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseUrl?: T['baseUrl']; + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Please don't use the Fetch client for Next.js applications. The `next` + * options won't have any effect. + * + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. + */ + next?: never; + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: + | 'arrayBuffer' + | 'auto' + | 'blob' + | 'formData' + | 'json' + | 'stream' + | 'text'; + /** + * Should we return only data or multiple fields (data, error, response, etc.)? + * + * @default 'fields' + */ + responseStyle?: ResponseStyle; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T['throwOnError']; } export interface RequestOptions< - TData = unknown, - TResponseStyle extends ResponseStyle = "fields", - ThrowOnError extends boolean = boolean, - Url extends string = string, + TData = unknown, + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, > extends Config<{ - responseStyle: TResponseStyle; - throwOnError: ThrowOnError; - }>, - Pick< - ServerSentEventsOptions, - "onSseError" | "onSseEvent" | "sseDefaultRetryDelay" | "sseMaxRetryAttempts" | "sseMaxRetryDelay" - > { - /** - * Any body that you want to add to your request. - * - * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} - */ - body?: unknown; - path?: Record; - query?: Record; - /** - * Security mechanism(s) to use for the request. - */ - security?: ReadonlyArray; - url: Url; + responseStyle: TResponseStyle; + throwOnError: ThrowOnError; + }>, + Pick< + ServerSentEventsOptions, + | 'onSseError' + | 'onSseEvent' + | 'sseDefaultRetryDelay' + | 'sseMaxRetryAttempts' + | 'sseMaxRetryDelay' + > { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; } export interface ResolvedRequestOptions< - TResponseStyle extends ResponseStyle = "fields", - ThrowOnError extends boolean = boolean, - Url extends string = string, + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, > extends RequestOptions { - serializedBody?: string; + serializedBody?: string; } export type RequestResult< - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = boolean, - TResponseStyle extends ResponseStyle = "fields", + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', > = ThrowOnError extends true - ? Promise< - TResponseStyle extends "data" - ? TData extends Record - ? TData[keyof TData] - : TData - : { - data: TData extends Record ? TData[keyof TData] : TData; - request: Request; - response: Response; - } - > - : Promise< - TResponseStyle extends "data" - ? (TData extends Record ? TData[keyof TData] : TData) | undefined - : ( - | { - data: TData extends Record ? TData[keyof TData] : TData; - error: undefined; - } - | { - data: undefined; - error: TError extends Record ? TError[keyof TError] : TError; - } - ) & { - request: Request; - response: Response; - } - >; + ? Promise< + TResponseStyle extends 'data' + ? TData extends Record + ? TData[keyof TData] + : TData + : { + data: TData extends Record + ? TData[keyof TData] + : TData; + request: Request; + response: Response; + } + > + : Promise< + TResponseStyle extends 'data' + ? + | (TData extends Record + ? TData[keyof TData] + : TData) + | undefined + : ( + | { + data: TData extends Record + ? TData[keyof TData] + : TData; + error: undefined; + } + | { + data: undefined; + error: TError extends Record + ? TError[keyof TError] + : TError; + } + ) & { + request: Request; + response: Response; + } + >; export interface ClientOptions { - baseUrl?: string; - responseStyle?: ResponseStyle; - throwOnError?: boolean; + baseUrl?: string; + responseStyle?: ResponseStyle; + throwOnError?: boolean; } type MethodFn = < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, - TResponseStyle extends ResponseStyle = "fields", + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', >( - options: Omit, "method">, + options: Omit, 'method'>, ) => RequestResult; type SseFn = < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, - TResponseStyle extends ResponseStyle = "fields", + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', >( - options: Omit, "method">, + options: Omit, 'method'>, ) => Promise>; type RequestFn = < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, - TResponseStyle extends ResponseStyle = "fields", + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', >( - options: Omit, "method"> & - Pick>, "method">, + options: Omit, 'method'> & + Pick< + Required>, + 'method' + >, ) => RequestResult; type BuildUrlFn = < - TData extends { - body?: unknown; - path?: Record; - query?: Record; - url: string; - }, + TData extends { + body?: unknown; + path?: Record; + query?: Record; + url: string; + }, >( - options: TData & Options, + options: TData & Options, ) => string; -export type Client = CoreClient & { - interceptors: Middleware; +export type Client = CoreClient< + RequestFn, + Config, + MethodFn, + BuildUrlFn, + SseFn +> & { + interceptors: Middleware; }; /** @@ -180,23 +216,26 @@ export type Client = CoreClient * to ensure your client always has the correct values. */ export type CreateClientConfig = ( - override?: Config, + override?: Config, ) => Config & T>; export interface TDataShape { - body?: unknown; - headers?: unknown; - path?: unknown; - query?: unknown; - url: string; + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; } type OmitKeys = Pick>; export type Options< - TData extends TDataShape = TDataShape, - ThrowOnError extends boolean = boolean, - TResponse = unknown, - TResponseStyle extends ResponseStyle = "fields", -> = OmitKeys, "body" | "path" | "query" | "url"> & - ([TData] extends [never] ? unknown : Omit); + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, + TResponse = unknown, + TResponseStyle extends ResponseStyle = 'fields', +> = OmitKeys< + RequestOptions, + 'body' | 'path' | 'query' | 'url' +> & + ([TData] extends [never] ? unknown : Omit); diff --git a/app/client/api-client/client/utils.gen.ts b/app/client/api-client/client/utils.gen.ts index fb46044..4c48a9e 100644 --- a/app/client/api-client/client/utils.gen.ts +++ b/app/client/api-client/client/utils.gen.ts @@ -1,289 +1,332 @@ // This file is auto-generated by @hey-api/openapi-ts -import { getAuthToken } from "../core/auth.gen"; -import type { QuerySerializerOptions } from "../core/bodySerializer.gen"; -import { jsonBodySerializer } from "../core/bodySerializer.gen"; -import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam } from "../core/pathSerializer.gen"; -import { getUrl } from "../core/utils.gen"; -import type { Client, ClientOptions, Config, RequestOptions } from "./types.gen"; +import { getAuthToken } from '../core/auth.gen'; +import type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +import { jsonBodySerializer } from '../core/bodySerializer.gen'; +import { + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '../core/pathSerializer.gen'; +import { getUrl } from '../core/utils.gen'; +import type { Client, ClientOptions, Config, RequestOptions } from './types.gen'; -export const createQuerySerializer = ({ parameters = {}, ...args }: QuerySerializerOptions = {}) => { - const querySerializer = (queryParams: T) => { - const search: string[] = []; - if (queryParams && typeof queryParams === "object") { - for (const name in queryParams) { - const value = queryParams[name]; +export const createQuerySerializer = ({ + parameters = {}, + ...args +}: QuerySerializerOptions = {}) => { + const querySerializer = (queryParams: T) => { + const search: string[] = []; + if (queryParams && typeof queryParams === 'object') { + for (const name in queryParams) { + const value = queryParams[name]; - if (value === undefined || value === null) { - continue; - } + if (value === undefined || value === null) { + continue; + } - const options = parameters[name] || args; + const options = parameters[name] || args; - if (Array.isArray(value)) { - const serializedArray = serializeArrayParam({ - allowReserved: options.allowReserved, - explode: true, - name, - style: "form", - value, - ...options.array, - }); - if (serializedArray) search.push(serializedArray); - } else if (typeof value === "object") { - const serializedObject = serializeObjectParam({ - allowReserved: options.allowReserved, - explode: true, - name, - style: "deepObject", - value: value as Record, - ...options.object, - }); - if (serializedObject) search.push(serializedObject); - } else { - const serializedPrimitive = serializePrimitiveParam({ - allowReserved: options.allowReserved, - name, - value: value as string, - }); - if (serializedPrimitive) search.push(serializedPrimitive); - } - } - } - return search.join("&"); - }; - return querySerializer; + if (Array.isArray(value)) { + const serializedArray = serializeArrayParam({ + allowReserved: options.allowReserved, + explode: true, + name, + style: 'form', + value, + ...options.array, + }); + if (serializedArray) search.push(serializedArray); + } else if (typeof value === 'object') { + const serializedObject = serializeObjectParam({ + allowReserved: options.allowReserved, + explode: true, + name, + style: 'deepObject', + value: value as Record, + ...options.object, + }); + if (serializedObject) search.push(serializedObject); + } else { + const serializedPrimitive = serializePrimitiveParam({ + allowReserved: options.allowReserved, + name, + value: value as string, + }); + if (serializedPrimitive) search.push(serializedPrimitive); + } + } + } + return search.join('&'); + }; + return querySerializer; }; /** * Infers parseAs value from provided Content-Type header. */ -export const getParseAs = (contentType: string | null): Exclude => { - if (!contentType) { - // If no Content-Type header is provided, the best we can do is return the raw response body, - // which is effectively the same as the 'stream' option. - return "stream"; - } +export const getParseAs = ( + contentType: string | null, +): Exclude => { + if (!contentType) { + // If no Content-Type header is provided, the best we can do is return the raw response body, + // which is effectively the same as the 'stream' option. + return 'stream'; + } - const cleanContent = contentType.split(";")[0]?.trim(); + const cleanContent = contentType.split(';')[0]?.trim(); - if (!cleanContent) { - return; - } + if (!cleanContent) { + return; + } - if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) { - return "json"; - } + if ( + cleanContent.startsWith('application/json') || + cleanContent.endsWith('+json') + ) { + return 'json'; + } - if (cleanContent === "multipart/form-data") { - return "formData"; - } + if (cleanContent === 'multipart/form-data') { + return 'formData'; + } - if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) { - return "blob"; - } + if ( + ['application/', 'audio/', 'image/', 'video/'].some((type) => + cleanContent.startsWith(type), + ) + ) { + return 'blob'; + } - if (cleanContent.startsWith("text/")) { - return "text"; - } + if (cleanContent.startsWith('text/')) { + return 'text'; + } - return; + return; }; const checkForExistence = ( - options: Pick & { - headers: Headers; - }, - name?: string, + options: Pick & { + headers: Headers; + }, + name?: string, ): boolean => { - if (!name) { - return false; - } - if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) { - return true; - } - return false; + if (!name) { + return false; + } + if ( + options.headers.has(name) || + options.query?.[name] || + options.headers.get('Cookie')?.includes(`${name}=`) + ) { + return true; + } + return false; }; export const setAuthParams = async ({ - security, - ...options -}: Pick, "security"> & - Pick & { - headers: Headers; - }) => { - for (const auth of security) { - if (checkForExistence(options, auth.name)) { - continue; - } + security, + ...options +}: Pick, 'security'> & + Pick & { + headers: Headers; + }) => { + for (const auth of security) { + if (checkForExistence(options, auth.name)) { + continue; + } - const token = await getAuthToken(auth, options.auth); + const token = await getAuthToken(auth, options.auth); - if (!token) { - continue; - } + if (!token) { + continue; + } - const name = auth.name ?? "Authorization"; + const name = auth.name ?? 'Authorization'; - switch (auth.in) { - case "query": - if (!options.query) { - options.query = {}; - } - options.query[name] = token; - break; - case "cookie": - options.headers.append("Cookie", `${name}=${token}`); - break; - case "header": - default: - options.headers.set(name, token); - break; - } - } + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'cookie': + options.headers.append('Cookie', `${name}=${token}`); + break; + case 'header': + default: + options.headers.set(name, token); + break; + } + } }; -export const buildUrl: Client["buildUrl"] = (options) => - getUrl({ - baseUrl: options.baseUrl as string, - path: options.path, - query: options.query, - querySerializer: - typeof options.querySerializer === "function" - ? options.querySerializer - : createQuerySerializer(options.querySerializer), - url: options.url, - }); +export const buildUrl: Client['buildUrl'] = (options) => + getUrl({ + baseUrl: options.baseUrl as string, + path: options.path, + query: options.query, + querySerializer: + typeof options.querySerializer === 'function' + ? options.querySerializer + : createQuerySerializer(options.querySerializer), + url: options.url, + }); export const mergeConfigs = (a: Config, b: Config): Config => { - const config = { ...a, ...b }; - if (config.baseUrl?.endsWith("/")) { - config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); - } - config.headers = mergeHeaders(a.headers, b.headers); - return config; + const config = { ...a, ...b }; + if (config.baseUrl?.endsWith('/')) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); + } + config.headers = mergeHeaders(a.headers, b.headers); + return config; }; const headersEntries = (headers: Headers): Array<[string, string]> => { - const entries: Array<[string, string]> = []; - headers.forEach((value, key) => { - entries.push([key, value]); - }); - return entries; + const entries: Array<[string, string]> = []; + headers.forEach((value, key) => { + entries.push([key, value]); + }); + return entries; }; -export const mergeHeaders = (...headers: Array["headers"] | undefined>): Headers => { - const mergedHeaders = new Headers(); - for (const header of headers) { - if (!header) { - continue; - } +export const mergeHeaders = ( + ...headers: Array['headers'] | undefined> +): Headers => { + const mergedHeaders = new Headers(); + for (const header of headers) { + if (!header) { + continue; + } - const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header); + const iterator = + header instanceof Headers + ? headersEntries(header) + : Object.entries(header); - for (const [key, value] of iterator) { - if (value === null) { - mergedHeaders.delete(key); - } else if (Array.isArray(value)) { - for (const v of value) { - mergedHeaders.append(key, v as string); - } - } else if (value !== undefined) { - // assume object headers are meant to be JSON stringified, i.e. their - // content value in OpenAPI specification is 'application/json' - mergedHeaders.set(key, typeof value === "object" ? JSON.stringify(value) : (value as string)); - } - } - } - return mergedHeaders; + for (const [key, value] of iterator) { + if (value === null) { + mergedHeaders.delete(key); + } else if (Array.isArray(value)) { + for (const v of value) { + mergedHeaders.append(key, v as string); + } + } else if (value !== undefined) { + // assume object headers are meant to be JSON stringified, i.e. their + // content value in OpenAPI specification is 'application/json' + mergedHeaders.set( + key, + typeof value === 'object' ? JSON.stringify(value) : (value as string), + ); + } + } + } + return mergedHeaders; }; type ErrInterceptor = ( - error: Err, - response: Res, - request: Req, - options: Options, + error: Err, + response: Res, + request: Req, + options: Options, ) => Err | Promise; -type ReqInterceptor = (request: Req, options: Options) => Req | Promise; +type ReqInterceptor = ( + request: Req, + options: Options, +) => Req | Promise; -type ResInterceptor = (response: Res, request: Req, options: Options) => Res | Promise; +type ResInterceptor = ( + response: Res, + request: Req, + options: Options, +) => Res | Promise; class Interceptors { - fns: Array = []; + fns: Array = []; - clear(): void { - this.fns = []; - } + clear(): void { + this.fns = []; + } - eject(id: number | Interceptor): void { - const index = this.getInterceptorIndex(id); - if (this.fns[index]) { - this.fns[index] = null; - } - } + eject(id: number | Interceptor): void { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = null; + } + } - exists(id: number | Interceptor): boolean { - const index = this.getInterceptorIndex(id); - return Boolean(this.fns[index]); - } + exists(id: number | Interceptor): boolean { + const index = this.getInterceptorIndex(id); + return Boolean(this.fns[index]); + } - getInterceptorIndex(id: number | Interceptor): number { - if (typeof id === "number") { - return this.fns[id] ? id : -1; - } - return this.fns.indexOf(id); - } + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === 'number') { + return this.fns[id] ? id : -1; + } + return this.fns.indexOf(id); + } - update(id: number | Interceptor, fn: Interceptor): number | Interceptor | false { - const index = this.getInterceptorIndex(id); - if (this.fns[index]) { - this.fns[index] = fn; - return id; - } - return false; - } + update( + id: number | Interceptor, + fn: Interceptor, + ): number | Interceptor | false { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = fn; + return id; + } + return false; + } - use(fn: Interceptor): number { - this.fns.push(fn); - return this.fns.length - 1; - } + use(fn: Interceptor): number { + this.fns.push(fn); + return this.fns.length - 1; + } } export interface Middleware { - error: Interceptors>; - request: Interceptors>; - response: Interceptors>; + error: Interceptors>; + request: Interceptors>; + response: Interceptors>; } -export const createInterceptors = (): Middleware => ({ - error: new Interceptors>(), - request: new Interceptors>(), - response: new Interceptors>(), +export const createInterceptors = (): Middleware< + Req, + Res, + Err, + Options +> => ({ + error: new Interceptors>(), + request: new Interceptors>(), + response: new Interceptors>(), }); const defaultQuerySerializer = createQuerySerializer({ - allowReserved: false, - array: { - explode: true, - style: "form", - }, - object: { - explode: true, - style: "deepObject", - }, + allowReserved: false, + array: { + explode: true, + style: 'form', + }, + object: { + explode: true, + style: 'deepObject', + }, }); const defaultHeaders = { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }; export const createConfig = ( - override: Config & T> = {}, + override: Config & T> = {}, ): Config & T> => ({ - ...jsonBodySerializer, - headers: defaultHeaders, - parseAs: "auto", - querySerializer: defaultQuerySerializer, - ...override, + ...jsonBodySerializer, + headers: defaultHeaders, + parseAs: 'auto', + querySerializer: defaultQuerySerializer, + ...override, }); diff --git a/app/client/api-client/core/auth.gen.ts b/app/client/api-client/core/auth.gen.ts index 9d80405..f8a7326 100644 --- a/app/client/api-client/core/auth.gen.ts +++ b/app/client/api-client/core/auth.gen.ts @@ -3,39 +3,40 @@ export type AuthToken = string | undefined; export interface Auth { - /** - * Which part of the request do we use to send the auth? - * - * @default 'header' - */ - in?: "header" | "query" | "cookie"; - /** - * Header or query parameter name. - * - * @default 'Authorization' - */ - name?: string; - scheme?: "basic" | "bearer"; - type: "apiKey" | "http"; + /** + * Which part of the request do we use to send the auth? + * + * @default 'header' + */ + in?: 'header' | 'query' | 'cookie'; + /** + * Header or query parameter name. + * + * @default 'Authorization' + */ + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; } export const getAuthToken = async ( - auth: Auth, - callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, + auth: Auth, + callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, ): Promise => { - const token = typeof callback === "function" ? await callback(auth) : callback; + const token = + typeof callback === 'function' ? await callback(auth) : callback; - if (!token) { - return; - } + if (!token) { + return; + } - if (auth.scheme === "bearer") { - return `Bearer ${token}`; - } + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; + } - if (auth.scheme === "basic") { - return `Basic ${btoa(token)}`; - } + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; + } - return token; + return token; }; diff --git a/app/client/api-client/core/bodySerializer.gen.ts b/app/client/api-client/core/bodySerializer.gen.ts index 10e7b3a..552b50f 100644 --- a/app/client/api-client/core/bodySerializer.gen.ts +++ b/app/client/api-client/core/bodySerializer.gen.ts @@ -1,82 +1,100 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { ArrayStyle, ObjectStyle, SerializerOptions } from "./pathSerializer.gen"; +import type { + ArrayStyle, + ObjectStyle, + SerializerOptions, +} from './pathSerializer.gen'; export type QuerySerializer = (query: Record) => string; export type BodySerializer = (body: any) => any; type QuerySerializerOptionsObject = { - allowReserved?: boolean; - array?: Partial>; - object?: Partial>; + allowReserved?: boolean; + array?: Partial>; + object?: Partial>; }; export type QuerySerializerOptions = QuerySerializerOptionsObject & { - /** - * Per-parameter serialization overrides. When provided, these settings - * override the global array/object settings for specific parameter names. - */ - parameters?: Record; + /** + * Per-parameter serialization overrides. When provided, these settings + * override the global array/object settings for specific parameter names. + */ + parameters?: Record; }; -const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => { - if (typeof value === "string" || value instanceof Blob) { - data.append(key, value); - } else if (value instanceof Date) { - data.append(key, value.toISOString()); - } else { - data.append(key, JSON.stringify(value)); - } +const serializeFormDataPair = ( + data: FormData, + key: string, + value: unknown, +): void => { + if (typeof value === 'string' || value instanceof Blob) { + data.append(key, value); + } else if (value instanceof Date) { + data.append(key, value.toISOString()); + } else { + data.append(key, JSON.stringify(value)); + } }; -const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value: unknown): void => { - if (typeof value === "string") { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } +const serializeUrlSearchParamsPair = ( + data: URLSearchParams, + key: string, + value: unknown, +): void => { + if (typeof value === 'string') { + data.append(key, value); + } else { + data.append(key, JSON.stringify(value)); + } }; export const formDataBodySerializer = { - bodySerializer: | Array>>(body: T): FormData => { - const data = new FormData(); + bodySerializer: | Array>>( + body: T, + ): FormData => { + const data = new FormData(); - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeFormDataPair(data, key, v)); - } else { - serializeFormDataPair(data, key, value); - } - }); + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeFormDataPair(data, key, v)); + } else { + serializeFormDataPair(data, key, value); + } + }); - return data; - }, + return data; + }, }; export const jsonBodySerializer = { - bodySerializer: (body: T): string => - JSON.stringify(body, (_key, value) => (typeof value === "bigint" ? value.toString() : value)), + bodySerializer: (body: T): string => + JSON.stringify(body, (_key, value) => + typeof value === 'bigint' ? value.toString() : value, + ), }; export const urlSearchParamsBodySerializer = { - bodySerializer: | Array>>(body: T): string => { - const data = new URLSearchParams(); + bodySerializer: | Array>>( + body: T, + ): string => { + const data = new URLSearchParams(); - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); - } else { - serializeUrlSearchParamsPair(data, key, value); - } - }); + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); + } else { + serializeUrlSearchParamsPair(data, key, value); + } + }); - return data.toString(); - }, + return data.toString(); + }, }; diff --git a/app/client/api-client/core/params.gen.ts b/app/client/api-client/core/params.gen.ts index c1fb15c..602715c 100644 --- a/app/client/api-client/core/params.gen.ts +++ b/app/client/api-client/core/params.gen.ts @@ -1,169 +1,176 @@ // This file is auto-generated by @hey-api/openapi-ts -type Slot = "body" | "headers" | "path" | "query"; +type Slot = 'body' | 'headers' | 'path' | 'query'; export type Field = - | { - in: Exclude; - /** - * Field name. This is the name we want the user to see and use. - */ - key: string; - /** - * Field mapped name. This is the name we want to use in the request. - * If omitted, we use the same value as `key`. - */ - map?: string; - } - | { - in: Extract; - /** - * Key isn't required for bodies. - */ - key?: string; - map?: string; - } - | { - /** - * Field name. This is the name we want the user to see and use. - */ - key: string; - /** - * Field mapped name. This is the name we want to use in the request. - * If `in` is omitted, `map` aliases `key` to the transport layer. - */ - map: Slot; - }; + | { + in: Exclude; + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If omitted, we use the same value as `key`. + */ + map?: string; + } + | { + in: Extract; + /** + * Key isn't required for bodies. + */ + key?: string; + map?: string; + } + | { + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If `in` is omitted, `map` aliases `key` to the transport layer. + */ + map: Slot; + }; export interface Fields { - allowExtra?: Partial>; - args?: ReadonlyArray; + allowExtra?: Partial>; + args?: ReadonlyArray; } export type FieldsConfig = ReadonlyArray; const extraPrefixesMap: Record = { - $body_: "body", - $headers_: "headers", - $path_: "path", - $query_: "query", + $body_: 'body', + $headers_: 'headers', + $path_: 'path', + $query_: 'query', }; const extraPrefixes = Object.entries(extraPrefixesMap); type KeyMap = Map< - string, - | { - in: Slot; - map?: string; - } - | { - in?: never; - map: Slot; - } + string, + | { + in: Slot; + map?: string; + } + | { + in?: never; + map: Slot; + } >; const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { - if (!map) { - map = new Map(); - } + if (!map) { + map = new Map(); + } - for (const config of fields) { - if ("in" in config) { - if (config.key) { - map.set(config.key, { - in: config.in, - map: config.map, - }); - } - } else if ("key" in config) { - map.set(config.key, { - map: config.map, - }); - } else if (config.args) { - buildKeyMap(config.args, map); - } - } + for (const config of fields) { + if ('in' in config) { + if (config.key) { + map.set(config.key, { + in: config.in, + map: config.map, + }); + } + } else if ('key' in config) { + map.set(config.key, { + map: config.map, + }); + } else if (config.args) { + buildKeyMap(config.args, map); + } + } - return map; + return map; }; interface Params { - body: unknown; - headers: Record; - path: Record; - query: Record; + body: unknown; + headers: Record; + path: Record; + query: Record; } const stripEmptySlots = (params: Params) => { - for (const [slot, value] of Object.entries(params)) { - if (value && typeof value === "object" && !Object.keys(value).length) { - delete params[slot as Slot]; - } - } + for (const [slot, value] of Object.entries(params)) { + if (value && typeof value === 'object' && !Object.keys(value).length) { + delete params[slot as Slot]; + } + } }; -export const buildClientParams = (args: ReadonlyArray, fields: FieldsConfig) => { - const params: Params = { - body: {}, - headers: {}, - path: {}, - query: {}, - }; +export const buildClientParams = ( + args: ReadonlyArray, + fields: FieldsConfig, +) => { + const params: Params = { + body: {}, + headers: {}, + path: {}, + query: {}, + }; - const map = buildKeyMap(fields); + const map = buildKeyMap(fields); - let config: FieldsConfig[number] | undefined; + let config: FieldsConfig[number] | undefined; - for (const [index, arg] of args.entries()) { - if (fields[index]) { - config = fields[index]; - } + for (const [index, arg] of args.entries()) { + if (fields[index]) { + config = fields[index]; + } - if (!config) { - continue; - } + if (!config) { + continue; + } - if ("in" in config) { - if (config.key) { - const field = map.get(config.key)!; - const name = field.map || config.key; - if (field.in) { - (params[field.in] as Record)[name] = arg; - } - } else { - params.body = arg; - } - } else { - for (const [key, value] of Object.entries(arg ?? {})) { - const field = map.get(key); + if ('in' in config) { + if (config.key) { + const field = map.get(config.key)!; + const name = field.map || config.key; + if (field.in) { + (params[field.in] as Record)[name] = arg; + } + } else { + params.body = arg; + } + } else { + for (const [key, value] of Object.entries(arg ?? {})) { + const field = map.get(key); - if (field) { - if (field.in) { - const name = field.map || key; - (params[field.in] as Record)[name] = value; - } else { - params[field.map] = value; - } - } else { - const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix)); + if (field) { + if (field.in) { + const name = field.map || key; + (params[field.in] as Record)[name] = value; + } else { + params[field.map] = value; + } + } else { + const extra = extraPrefixes.find(([prefix]) => + key.startsWith(prefix), + ); - if (extra) { - const [prefix, slot] = extra; - (params[slot] as Record)[key.slice(prefix.length)] = value; - } else if ("allowExtra" in config && config.allowExtra) { - for (const [slot, allowed] of Object.entries(config.allowExtra)) { - if (allowed) { - (params[slot as Slot] as Record)[key] = value; - break; - } - } - } - } - } - } - } + if (extra) { + const [prefix, slot] = extra; + (params[slot] as Record)[ + key.slice(prefix.length) + ] = value; + } else if ('allowExtra' in config && config.allowExtra) { + for (const [slot, allowed] of Object.entries(config.allowExtra)) { + if (allowed) { + (params[slot as Slot] as Record)[key] = value; + break; + } + } + } + } + } + } + } - stripEmptySlots(params); + stripEmptySlots(params); - return params; + return params; }; diff --git a/app/client/api-client/core/pathSerializer.gen.ts b/app/client/api-client/core/pathSerializer.gen.ts index 9e3cccd..8d99931 100644 --- a/app/client/api-client/core/pathSerializer.gen.ts +++ b/app/client/api-client/core/pathSerializer.gen.ts @@ -1,167 +1,181 @@ // This file is auto-generated by @hey-api/openapi-ts -interface SerializeOptions extends SerializePrimitiveOptions, SerializerOptions {} +interface SerializeOptions + extends SerializePrimitiveOptions, + SerializerOptions {} interface SerializePrimitiveOptions { - allowReserved?: boolean; - name: string; + allowReserved?: boolean; + name: string; } export interface SerializerOptions { - /** - * @default true - */ - explode: boolean; - style: T; + /** + * @default true + */ + explode: boolean; + style: T; } -export type ArrayStyle = "form" | "spaceDelimited" | "pipeDelimited"; +export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; -type MatrixStyle = "label" | "matrix" | "simple"; -export type ObjectStyle = "form" | "deepObject"; +type MatrixStyle = 'label' | 'matrix' | 'simple'; +export type ObjectStyle = 'form' | 'deepObject'; type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; interface SerializePrimitiveParam extends SerializePrimitiveOptions { - value: string; + value: string; } export const separatorArrayExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case "label": - return "."; - case "matrix": - return ";"; - case "simple": - return ","; - default: - return "&"; - } + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } }; export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case "form": - return ","; - case "pipeDelimited": - return "|"; - case "spaceDelimited": - return "%20"; - default: - return ","; - } + switch (style) { + case 'form': + return ','; + case 'pipeDelimited': + return '|'; + case 'spaceDelimited': + return '%20'; + default: + return ','; + } }; export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { - switch (style) { - case "label": - return "."; - case "matrix": - return ";"; - case "simple": - return ","; - default: - return "&"; - } + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } }; export const serializeArrayParam = ({ - allowReserved, - explode, - name, - style, - value, + allowReserved, + explode, + name, + style, + value, }: SerializeOptions & { - value: unknown[]; + value: unknown[]; }) => { - if (!explode) { - const joinedValues = (allowReserved ? value : value.map((v) => encodeURIComponent(v as string))).join( - separatorArrayNoExplode(style), - ); - switch (style) { - case "label": - return `.${joinedValues}`; - case "matrix": - return `;${name}=${joinedValues}`; - case "simple": - return joinedValues; - default: - return `${name}=${joinedValues}`; - } - } + if (!explode) { + const joinedValues = ( + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) + ).join(separatorArrayNoExplode(style)); + switch (style) { + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + case 'simple': + return joinedValues; + default: + return `${name}=${joinedValues}`; + } + } - const separator = separatorArrayExplode(style); - const joinedValues = value - .map((v) => { - if (style === "label" || style === "simple") { - return allowReserved ? v : encodeURIComponent(v as string); - } + const separator = separatorArrayExplode(style); + const joinedValues = value + .map((v) => { + if (style === 'label' || style === 'simple') { + return allowReserved ? v : encodeURIComponent(v as string); + } - return serializePrimitiveParam({ - allowReserved, - name, - value: v as string, - }); - }) - .join(separator); - return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues; + return serializePrimitiveParam({ + allowReserved, + name, + value: v as string, + }); + }) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; }; -export const serializePrimitiveParam = ({ allowReserved, name, value }: SerializePrimitiveParam) => { - if (value === undefined || value === null) { - return ""; - } +export const serializePrimitiveParam = ({ + allowReserved, + name, + value, +}: SerializePrimitiveParam) => { + if (value === undefined || value === null) { + return ''; + } - if (typeof value === "object") { - throw new Error( - "Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.", - ); - } + if (typeof value === 'object') { + throw new Error( + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', + ); + } - return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; }; export const serializeObjectParam = ({ - allowReserved, - explode, - name, - style, - value, - valueOnly, + allowReserved, + explode, + name, + style, + value, + valueOnly, }: SerializeOptions & { - value: Record | Date; - valueOnly?: boolean; + value: Record | Date; + valueOnly?: boolean; }) => { - if (value instanceof Date) { - return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; - } + if (value instanceof Date) { + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; + } - if (style !== "deepObject" && !explode) { - let values: string[] = []; - Object.entries(value).forEach(([key, v]) => { - values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)]; - }); - const joinedValues = values.join(","); - switch (style) { - case "form": - return `${name}=${joinedValues}`; - case "label": - return `.${joinedValues}`; - case "matrix": - return `;${name}=${joinedValues}`; - default: - return joinedValues; - } - } + if (style !== 'deepObject' && !explode) { + let values: string[] = []; + Object.entries(value).forEach(([key, v]) => { + values = [ + ...values, + key, + allowReserved ? (v as string) : encodeURIComponent(v as string), + ]; + }); + const joinedValues = values.join(','); + switch (style) { + case 'form': + return `${name}=${joinedValues}`; + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + default: + return joinedValues; + } + } - const separator = separatorObjectExplode(style); - const joinedValues = Object.entries(value) - .map(([key, v]) => - serializePrimitiveParam({ - allowReserved, - name: style === "deepObject" ? `${name}[${key}]` : key, - value: v as string, - }), - ) - .join(separator); - return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues; + const separator = separatorObjectExplode(style); + const joinedValues = Object.entries(value) + .map(([key, v]) => + serializePrimitiveParam({ + allowReserved, + name: style === 'deepObject' ? `${name}[${key}]` : key, + value: v as string, + }), + ) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; }; diff --git a/app/client/api-client/core/queryKeySerializer.gen.ts b/app/client/api-client/core/queryKeySerializer.gen.ts index b3e8767..d3bb683 100644 --- a/app/client/api-client/core/queryKeySerializer.gen.ts +++ b/app/client/api-client/core/queryKeySerializer.gen.ts @@ -3,109 +3,134 @@ /** * JSON-friendly union that mirrors what Pinia Colada can hash. */ -export type JsonValue = null | string | number | boolean | JsonValue[] | { [key: string]: JsonValue }; +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; /** * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. */ export const queryKeyJsonReplacer = (_key: string, value: unknown) => { - if (value === undefined || typeof value === "function" || typeof value === "symbol") { - return undefined; - } - if (typeof value === "bigint") { - return value.toString(); - } - if (value instanceof Date) { - return value.toISOString(); - } - return value; + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; }; /** * Safely stringifies a value and parses it back into a JsonValue. */ export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { - try { - const json = JSON.stringify(input, queryKeyJsonReplacer); - if (json === undefined) { - return undefined; - } - return JSON.parse(json) as JsonValue; - } catch { - return undefined; - } + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } }; /** * Detects plain objects (including objects with a null prototype). */ const isPlainObject = (value: unknown): value is Record => { - if (value === null || typeof value !== "object") { - return false; - } - const prototype = Object.getPrototypeOf(value as object); - return prototype === Object.prototype || prototype === null; + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; }; /** * Turns URLSearchParams into a sorted JSON object for deterministic keys. */ const serializeSearchParams = (params: URLSearchParams): JsonValue => { - const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b)); - const result: Record = {}; + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; - for (const [key, value] of entries) { - const existing = result[key]; - if (existing === undefined) { - result[key] = value; - continue; - } + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } - if (Array.isArray(existing)) { - (existing as string[]).push(value); - } else { - result[key] = [existing, value]; - } - } + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } - return result; + return result; }; /** * Normalizes any accepted value into a JSON-friendly shape for query keys. */ -export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => { - if (value === null) { - return null; - } +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } - if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { - return value; - } + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } - if (value === undefined || typeof value === "function" || typeof value === "symbol") { - return undefined; - } + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } - if (typeof value === "bigint") { - return value.toString(); - } + if (typeof value === 'bigint') { + return value.toString(); + } - if (value instanceof Date) { - return value.toISOString(); - } + if (value instanceof Date) { + return value.toISOString(); + } - if (Array.isArray(value)) { - return stringifyToJsonValue(value); - } + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } - if (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) { - return serializeSearchParams(value); - } + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } - if (isPlainObject(value)) { - return stringifyToJsonValue(value); - } + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } - return undefined; + return undefined; }; diff --git a/app/client/api-client/core/serverSentEvents.gen.ts b/app/client/api-client/core/serverSentEvents.gen.ts index 8a27c45..f8fd78e 100644 --- a/app/client/api-client/core/serverSentEvents.gen.ts +++ b/app/client/api-client/core/serverSentEvents.gen.ts @@ -1,237 +1,264 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Config } from "./types.gen"; +import type { Config } from './types.gen'; -export type ServerSentEventsOptions = Omit & - Pick & { - /** - * Fetch API implementation. You can use this option to provide a custom - * fetch instance. - * - * @default globalThis.fetch - */ - fetch?: typeof fetch; - /** - * Implementing clients can call request interceptors inside this hook. - */ - onRequest?: (url: string, init: RequestInit) => Promise; - /** - * Callback invoked when a network or parsing error occurs during streaming. - * - * This option applies only if the endpoint returns a stream of events. - * - * @param error The error that occurred. - */ - onSseError?: (error: unknown) => void; - /** - * Callback invoked when an event is streamed from the server. - * - * This option applies only if the endpoint returns a stream of events. - * - * @param event Event streamed from the server. - * @returns Nothing (void). - */ - onSseEvent?: (event: StreamEvent) => void; - serializedBody?: RequestInit["body"]; - /** - * Default retry delay in milliseconds. - * - * This option applies only if the endpoint returns a stream of events. - * - * @default 3000 - */ - sseDefaultRetryDelay?: number; - /** - * Maximum number of retry attempts before giving up. - */ - sseMaxRetryAttempts?: number; - /** - * Maximum retry delay in milliseconds. - * - * Applies only when exponential backoff is used. - * - * This option applies only if the endpoint returns a stream of events. - * - * @default 30000 - */ - sseMaxRetryDelay?: number; - /** - * Optional sleep function for retry backoff. - * - * Defaults to using `setTimeout`. - */ - sseSleepFn?: (ms: number) => Promise; - url: string; - }; +export type ServerSentEventsOptions = Omit< + RequestInit, + 'method' +> & + Pick & { + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Implementing clients can call request interceptors inside this hook. + */ + onRequest?: (url: string, init: RequestInit) => Promise; + /** + * Callback invoked when a network or parsing error occurs during streaming. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param error The error that occurred. + */ + onSseError?: (error: unknown) => void; + /** + * Callback invoked when an event is streamed from the server. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param event Event streamed from the server. + * @returns Nothing (void). + */ + onSseEvent?: (event: StreamEvent) => void; + serializedBody?: RequestInit['body']; + /** + * Default retry delay in milliseconds. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 3000 + */ + sseDefaultRetryDelay?: number; + /** + * Maximum number of retry attempts before giving up. + */ + sseMaxRetryAttempts?: number; + /** + * Maximum retry delay in milliseconds. + * + * Applies only when exponential backoff is used. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 30000 + */ + sseMaxRetryDelay?: number; + /** + * Optional sleep function for retry backoff. + * + * Defaults to using `setTimeout`. + */ + sseSleepFn?: (ms: number) => Promise; + url: string; + }; export interface StreamEvent { - data: TData; - event?: string; - id?: string; - retry?: number; + data: TData; + event?: string; + id?: string; + retry?: number; } -export type ServerSentEventsResult = { - stream: AsyncGenerator ? TData[keyof TData] : TData, TReturn, TNext>; +export type ServerSentEventsResult< + TData = unknown, + TReturn = void, + TNext = unknown, +> = { + stream: AsyncGenerator< + TData extends Record ? TData[keyof TData] : TData, + TReturn, + TNext + >; }; export const createSseClient = ({ - onRequest, - onSseError, - onSseEvent, - responseTransformer, - responseValidator, - sseDefaultRetryDelay, - sseMaxRetryAttempts, - sseMaxRetryDelay, - sseSleepFn, - url, - ...options + onRequest, + onSseError, + onSseEvent, + responseTransformer, + responseValidator, + sseDefaultRetryDelay, + sseMaxRetryAttempts, + sseMaxRetryDelay, + sseSleepFn, + url, + ...options }: ServerSentEventsOptions): ServerSentEventsResult => { - let lastEventId: string | undefined; + let lastEventId: string | undefined; - const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))); + const sleep = + sseSleepFn ?? + ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))); - const createStream = async function* () { - let retryDelay: number = sseDefaultRetryDelay ?? 3000; - let attempt = 0; - const signal = options.signal ?? new AbortController().signal; + const createStream = async function* () { + let retryDelay: number = sseDefaultRetryDelay ?? 3000; + let attempt = 0; + const signal = options.signal ?? new AbortController().signal; - while (true) { - if (signal.aborted) break; + while (true) { + if (signal.aborted) break; - attempt++; + attempt++; - const headers = - options.headers instanceof Headers - ? options.headers - : new Headers(options.headers as Record | undefined); + const headers = + options.headers instanceof Headers + ? options.headers + : new Headers(options.headers as Record | undefined); - if (lastEventId !== undefined) { - headers.set("Last-Event-ID", lastEventId); - } + if (lastEventId !== undefined) { + headers.set('Last-Event-ID', lastEventId); + } - try { - const requestInit: RequestInit = { - redirect: "follow", - ...options, - body: options.serializedBody, - headers, - signal, - }; - let request = new Request(url, requestInit); - if (onRequest) { - request = await onRequest(url, requestInit); - } - // fetch must be assigned here, otherwise it would throw the error: - // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation - const _fetch = options.fetch ?? globalThis.fetch; - const response = await _fetch(request); + try { + const requestInit: RequestInit = { + redirect: 'follow', + ...options, + body: options.serializedBody, + headers, + signal, + }; + let request = new Request(url, requestInit); + if (onRequest) { + request = await onRequest(url, requestInit); + } + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = options.fetch ?? globalThis.fetch; + const response = await _fetch(request); - if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`); + if (!response.ok) + throw new Error( + `SSE failed: ${response.status} ${response.statusText}`, + ); - if (!response.body) throw new Error("No body in SSE response"); + if (!response.body) throw new Error('No body in SSE response'); - const reader = response.body.pipeThrough(new TextDecoderStream()).getReader(); + const reader = response.body + .pipeThrough(new TextDecoderStream()) + .getReader(); - let buffer = ""; + let buffer = ''; - const abortHandler = () => { - try { - reader.cancel(); - } catch { - // noop - } - }; + const abortHandler = () => { + try { + reader.cancel(); + } catch { + // noop + } + }; - signal.addEventListener("abort", abortHandler); + signal.addEventListener('abort', abortHandler); - try { - while (true) { - const { done, value } = await reader.read(); - if (done) break; - buffer += value; + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += value; - const chunks = buffer.split("\n\n"); - buffer = chunks.pop() ?? ""; + const chunks = buffer.split('\n\n'); + buffer = chunks.pop() ?? ''; - for (const chunk of chunks) { - const lines = chunk.split("\n"); - const dataLines: Array = []; - let eventName: string | undefined; + for (const chunk of chunks) { + const lines = chunk.split('\n'); + const dataLines: Array = []; + let eventName: string | undefined; - for (const line of lines) { - if (line.startsWith("data:")) { - dataLines.push(line.replace(/^data:\s*/, "")); - } else if (line.startsWith("event:")) { - eventName = line.replace(/^event:\s*/, ""); - } else if (line.startsWith("id:")) { - lastEventId = line.replace(/^id:\s*/, ""); - } else if (line.startsWith("retry:")) { - const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10); - if (!Number.isNaN(parsed)) { - retryDelay = parsed; - } - } - } + for (const line of lines) { + if (line.startsWith('data:')) { + dataLines.push(line.replace(/^data:\s*/, '')); + } else if (line.startsWith('event:')) { + eventName = line.replace(/^event:\s*/, ''); + } else if (line.startsWith('id:')) { + lastEventId = line.replace(/^id:\s*/, ''); + } else if (line.startsWith('retry:')) { + const parsed = Number.parseInt( + line.replace(/^retry:\s*/, ''), + 10, + ); + if (!Number.isNaN(parsed)) { + retryDelay = parsed; + } + } + } - let data: unknown; - let parsedJson = false; + let data: unknown; + let parsedJson = false; - if (dataLines.length) { - const rawData = dataLines.join("\n"); - try { - data = JSON.parse(rawData); - parsedJson = true; - } catch { - data = rawData; - } - } + if (dataLines.length) { + const rawData = dataLines.join('\n'); + try { + data = JSON.parse(rawData); + parsedJson = true; + } catch { + data = rawData; + } + } - if (parsedJson) { - if (responseValidator) { - await responseValidator(data); - } + if (parsedJson) { + if (responseValidator) { + await responseValidator(data); + } - if (responseTransformer) { - data = await responseTransformer(data); - } - } + if (responseTransformer) { + data = await responseTransformer(data); + } + } - onSseEvent?.({ - data, - event: eventName, - id: lastEventId, - retry: retryDelay, - }); + onSseEvent?.({ + data, + event: eventName, + id: lastEventId, + retry: retryDelay, + }); - if (dataLines.length) { - yield data as any; - } - } - } - } finally { - signal.removeEventListener("abort", abortHandler); - reader.releaseLock(); - } + if (dataLines.length) { + yield data as any; + } + } + } + } finally { + signal.removeEventListener('abort', abortHandler); + reader.releaseLock(); + } - break; // exit loop on normal completion - } catch (error) { - // connection failed or aborted; retry after delay - onSseError?.(error); + break; // exit loop on normal completion + } catch (error) { + // connection failed or aborted; retry after delay + onSseError?.(error); - if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) { - break; // stop after firing error - } + if ( + sseMaxRetryAttempts !== undefined && + attempt >= sseMaxRetryAttempts + ) { + break; // stop after firing error + } - // exponential backoff: double retry each attempt, cap at 30s - const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000); - await sleep(backoff); - } - } - }; + // exponential backoff: double retry each attempt, cap at 30s + const backoff = Math.min( + retryDelay * 2 ** (attempt - 1), + sseMaxRetryDelay ?? 30000, + ); + await sleep(backoff); + } + } + }; - const stream = createStream(); + const stream = createStream(); - return { stream }; + return { stream }; }; diff --git a/app/client/api-client/core/types.gen.ts b/app/client/api-client/core/types.gen.ts index 62252e5..643c070 100644 --- a/app/client/api-client/core/types.gen.ts +++ b/app/client/api-client/core/types.gen.ts @@ -1,86 +1,118 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Auth, AuthToken } from "./auth.gen"; -import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer.gen"; +import type { Auth, AuthToken } from './auth.gen'; +import type { + BodySerializer, + QuerySerializer, + QuerySerializerOptions, +} from './bodySerializer.gen'; -export type HttpMethod = "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace"; +export type HttpMethod = + | 'connect' + | 'delete' + | 'get' + | 'head' + | 'options' + | 'patch' + | 'post' + | 'put' + | 'trace'; -export type Client = { - /** - * Returns the final request URL. - */ - buildUrl: BuildUrlFn; - getConfig: () => Config; - request: RequestFn; - setConfig: (config: Config) => Config; +export type Client< + RequestFn = never, + Config = unknown, + MethodFn = never, + BuildUrlFn = never, + SseFn = never, +> = { + /** + * Returns the final request URL. + */ + buildUrl: BuildUrlFn; + getConfig: () => Config; + request: RequestFn; + setConfig: (config: Config) => Config; } & { - [K in HttpMethod]: MethodFn; -} & ([SseFn] extends [never] ? { sse?: never } : { sse: { [K in HttpMethod]: SseFn } }); + [K in HttpMethod]: MethodFn; +} & ([SseFn] extends [never] + ? { sse?: never } + : { sse: { [K in HttpMethod]: SseFn } }); export interface Config { - /** - * Auth token or a function returning auth token. The resolved value will be - * added to the request payload as defined by its `security` array. - */ - auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; - /** - * A function for serializing request body parameter. By default, - * {@link JSON.stringify()} will be used. - */ - bodySerializer?: BodySerializer | null; - /** - * An object containing any HTTP headers that you want to pre-populate your - * `Headers` object with. - * - * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} - */ - headers?: - | RequestInit["headers"] - | Record; - /** - * The request method. - * - * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} - */ - method?: Uppercase; - /** - * A function for serializing request query parameters. By default, arrays - * will be exploded in form style, objects will be exploded in deepObject - * style, and reserved characters are percent-encoded. - * - * This method will have no effect if the native `paramsSerializer()` Axios - * API function is used. - * - * {@link https://swagger.io/docs/specification/serialization/#query View examples} - */ - querySerializer?: QuerySerializer | QuerySerializerOptions; - /** - * A function validating request data. This is useful if you want to ensure - * the request conforms to the desired shape, so it can be safely sent to - * the server. - */ - requestValidator?: (data: unknown) => Promise; - /** - * A function transforming response data before it's returned. This is useful - * for post-processing data, e.g. converting ISO strings into Date objects. - */ - responseTransformer?: (data: unknown) => Promise; - /** - * A function validating response data. This is useful if you want to ensure - * the response conforms to the desired shape, so it can be safely passed to - * the transformers and returned to the user. - */ - responseValidator?: (data: unknown) => Promise; + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: + | RequestInit['headers'] + | Record< + string, + | string + | number + | boolean + | (string | number | boolean)[] + | null + | undefined + | unknown + >; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: Uppercase; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function validating request data. This is useful if you want to ensure + * the request conforms to the desired shape, so it can be safely sent to + * the server. + */ + requestValidator?: (data: unknown) => Promise; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; } type IsExactlyNeverOrNeverUndefined = [T] extends [never] - ? true - : [T] extends [never | undefined] - ? [undefined] extends [T] - ? false - : true - : false; + ? true + : [T] extends [never | undefined] + ? [undefined] extends [T] + ? false + : true + : false; export type OmitNever> = { - [K in keyof T as IsExactlyNeverOrNeverUndefined extends true ? never : K]: T[K]; + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true + ? never + : K]: T[K]; }; diff --git a/app/client/api-client/core/utils.gen.ts b/app/client/api-client/core/utils.gen.ts index 7e48839..0b5389d 100644 --- a/app/client/api-client/core/utils.gen.ts +++ b/app/client/api-client/core/utils.gen.ts @@ -1,137 +1,143 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { BodySerializer, QuerySerializer } from "./bodySerializer.gen"; +import type { BodySerializer, QuerySerializer } from './bodySerializer.gen'; import { - type ArraySeparatorStyle, - serializeArrayParam, - serializeObjectParam, - serializePrimitiveParam, -} from "./pathSerializer.gen"; + type ArraySeparatorStyle, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from './pathSerializer.gen'; export interface PathSerializer { - path: Record; - url: string; + path: Record; + url: string; } export const PATH_PARAM_RE = /\{[^{}]+\}/g; export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { - let url = _url; - const matches = _url.match(PATH_PARAM_RE); - if (matches) { - for (const match of matches) { - let explode = false; - let name = match.substring(1, match.length - 1); - let style: ArraySeparatorStyle = "simple"; + let url = _url; + const matches = _url.match(PATH_PARAM_RE); + if (matches) { + for (const match of matches) { + let explode = false; + let name = match.substring(1, match.length - 1); + let style: ArraySeparatorStyle = 'simple'; - if (name.endsWith("*")) { - explode = true; - name = name.substring(0, name.length - 1); - } + if (name.endsWith('*')) { + explode = true; + name = name.substring(0, name.length - 1); + } - if (name.startsWith(".")) { - name = name.substring(1); - style = "label"; - } else if (name.startsWith(";")) { - name = name.substring(1); - style = "matrix"; - } + if (name.startsWith('.')) { + name = name.substring(1); + style = 'label'; + } else if (name.startsWith(';')) { + name = name.substring(1); + style = 'matrix'; + } - const value = path[name]; + const value = path[name]; - if (value === undefined || value === null) { - continue; - } + if (value === undefined || value === null) { + continue; + } - if (Array.isArray(value)) { - url = url.replace(match, serializeArrayParam({ explode, name, style, value })); - continue; - } + if (Array.isArray(value)) { + url = url.replace( + match, + serializeArrayParam({ explode, name, style, value }), + ); + continue; + } - if (typeof value === "object") { - url = url.replace( - match, - serializeObjectParam({ - explode, - name, - style, - value: value as Record, - valueOnly: true, - }), - ); - continue; - } + if (typeof value === 'object') { + url = url.replace( + match, + serializeObjectParam({ + explode, + name, + style, + value: value as Record, + valueOnly: true, + }), + ); + continue; + } - if (style === "matrix") { - url = url.replace( - match, - `;${serializePrimitiveParam({ - name, - value: value as string, - })}`, - ); - continue; - } + if (style === 'matrix') { + url = url.replace( + match, + `;${serializePrimitiveParam({ + name, + value: value as string, + })}`, + ); + continue; + } - const replaceValue = encodeURIComponent(style === "label" ? `.${value as string}` : (value as string)); - url = url.replace(match, replaceValue); - } - } - return url; + const replaceValue = encodeURIComponent( + style === 'label' ? `.${value as string}` : (value as string), + ); + url = url.replace(match, replaceValue); + } + } + return url; }; export const getUrl = ({ - baseUrl, - path, - query, - querySerializer, - url: _url, + baseUrl, + path, + query, + querySerializer, + url: _url, }: { - baseUrl?: string; - path?: Record; - query?: Record; - querySerializer: QuerySerializer; - url: string; + baseUrl?: string; + path?: Record; + query?: Record; + querySerializer: QuerySerializer; + url: string; }) => { - const pathUrl = _url.startsWith("/") ? _url : `/${_url}`; - let url = (baseUrl ?? "") + pathUrl; - if (path) { - url = defaultPathSerializer({ path, url }); - } - let search = query ? querySerializer(query) : ""; - if (search.startsWith("?")) { - search = search.substring(1); - } - if (search) { - url += `?${search}`; - } - return url; + const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; + let url = (baseUrl ?? '') + pathUrl; + if (path) { + url = defaultPathSerializer({ path, url }); + } + let search = query ? querySerializer(query) : ''; + if (search.startsWith('?')) { + search = search.substring(1); + } + if (search) { + url += `?${search}`; + } + return url; }; export function getValidRequestBody(options: { - body?: unknown; - bodySerializer?: BodySerializer | null; - serializedBody?: unknown; + body?: unknown; + bodySerializer?: BodySerializer | null; + serializedBody?: unknown; }) { - const hasBody = options.body !== undefined; - const isSerializedBody = hasBody && options.bodySerializer; + const hasBody = options.body !== undefined; + const isSerializedBody = hasBody && options.bodySerializer; - if (isSerializedBody) { - if ("serializedBody" in options) { - const hasSerializedBody = options.serializedBody !== undefined && options.serializedBody !== ""; + if (isSerializedBody) { + if ('serializedBody' in options) { + const hasSerializedBody = + options.serializedBody !== undefined && options.serializedBody !== ''; - return hasSerializedBody ? options.serializedBody : null; - } + return hasSerializedBody ? options.serializedBody : null; + } - // not all clients implement a serializedBody property (i.e. client-axios) - return options.body !== "" ? options.body : null; - } + // not all clients implement a serializedBody property (i.e. client-axios) + return options.body !== '' ? options.body : null; + } - // plain/text body - if (hasBody) { - return options.body; - } + // plain/text body + if (hasBody) { + return options.body; + } - // no body was provided - return undefined; + // no body was provided + return undefined; } diff --git a/app/client/api-client/index.ts b/app/client/api-client/index.ts index 7a71692..c352c10 100644 --- a/app/client/api-client/index.ts +++ b/app/client/api-client/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export type * from "./types.gen"; -export * from "./sdk.gen"; +export type * from './types.gen'; +export * from './sdk.gen'; diff --git a/app/client/api-client/sdk.gen.ts b/app/client/api-client/sdk.gen.ts index 891623b..3e20b8c 100644 --- a/app/client/api-client/sdk.gen.ts +++ b/app/client/api-client/sdk.gen.ts @@ -1,588 +1,453 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Client, Options as Options2, TDataShape } from "./client"; -import { client } from "./client.gen"; -import type { - BrowseFilesystemData, - BrowseFilesystemResponses, - ChangePasswordData, - ChangePasswordResponses, - CreateBackupScheduleData, - CreateBackupScheduleResponses, - CreateRepositoryData, - CreateRepositoryResponses, - CreateVolumeData, - CreateVolumeResponses, - DeleteBackupScheduleData, - DeleteBackupScheduleResponses, - DeleteRepositoryData, - DeleteRepositoryResponses, - DeleteVolumeData, - DeleteVolumeResponses, - DoctorRepositoryData, - DoctorRepositoryResponses, - DownloadResticPasswordData, - DownloadResticPasswordResponses, - GetBackupScheduleData, - GetBackupScheduleForVolumeData, - GetBackupScheduleForVolumeResponses, - GetBackupScheduleResponses, - GetContainersUsingVolumeData, - GetContainersUsingVolumeErrors, - GetContainersUsingVolumeResponses, - GetMeData, - GetMeResponses, - GetRepositoryData, - GetRepositoryResponses, - GetSnapshotDetailsData, - GetSnapshotDetailsResponses, - GetStatusData, - GetStatusResponses, - GetSystemInfoData, - GetSystemInfoResponses, - GetVolumeData, - GetVolumeErrors, - GetVolumeResponses, - HealthCheckVolumeData, - HealthCheckVolumeErrors, - HealthCheckVolumeResponses, - ListBackupSchedulesData, - ListBackupSchedulesResponses, - ListFilesData, - ListFilesResponses, - ListRcloneRemotesData, - ListRcloneRemotesResponses, - ListRepositoriesData, - ListRepositoriesResponses, - ListSnapshotFilesData, - ListSnapshotFilesResponses, - ListSnapshotsData, - ListSnapshotsResponses, - ListVolumesData, - ListVolumesResponses, - LoginData, - LoginResponses, - LogoutData, - LogoutResponses, - MountVolumeData, - MountVolumeResponses, - RegisterData, - RegisterResponses, - RestoreSnapshotData, - RestoreSnapshotResponses, - RunBackupNowData, - RunBackupNowResponses, - StopBackupData, - StopBackupErrors, - StopBackupResponses, - TestConnectionData, - TestConnectionResponses, - UnmountVolumeData, - UnmountVolumeResponses, - UpdateBackupScheduleData, - UpdateBackupScheduleResponses, - UpdateVolumeData, - UpdateVolumeErrors, - UpdateVolumeResponses, -} from "./types.gen"; +import type { Client, Options as Options2, TDataShape } from './client'; +import { client } from './client.gen'; +import type { BrowseFilesystemData, BrowseFilesystemResponses, ChangePasswordData, ChangePasswordResponses, CreateBackupScheduleData, CreateBackupScheduleResponses, CreateRepositoryData, CreateRepositoryResponses, CreateVolumeData, CreateVolumeResponses, DeleteBackupScheduleData, DeleteBackupScheduleResponses, DeleteRepositoryData, DeleteRepositoryResponses, DeleteVolumeData, DeleteVolumeResponses, DoctorRepositoryData, DoctorRepositoryResponses, DownloadResticPasswordData, DownloadResticPasswordResponses, GetBackupScheduleData, GetBackupScheduleForVolumeData, GetBackupScheduleForVolumeResponses, GetBackupScheduleResponses, GetContainersUsingVolumeData, GetContainersUsingVolumeErrors, GetContainersUsingVolumeResponses, GetMeData, GetMeResponses, GetRepositoryData, GetRepositoryResponses, GetSnapshotDetailsData, GetSnapshotDetailsResponses, GetStatusData, GetStatusResponses, GetSystemInfoData, GetSystemInfoResponses, GetVolumeData, GetVolumeErrors, GetVolumeResponses, HealthCheckVolumeData, HealthCheckVolumeErrors, HealthCheckVolumeResponses, ListBackupSchedulesData, ListBackupSchedulesResponses, ListFilesData, ListFilesResponses, ListRcloneRemotesData, ListRcloneRemotesResponses, ListRepositoriesData, ListRepositoriesResponses, ListSnapshotFilesData, ListSnapshotFilesResponses, ListSnapshotsData, ListSnapshotsResponses, ListVolumesData, ListVolumesResponses, LoginData, LoginResponses, LogoutData, LogoutResponses, MountVolumeData, MountVolumeResponses, RegisterData, RegisterResponses, RestoreSnapshotData, RestoreSnapshotResponses, RunBackupNowData, RunBackupNowResponses, RunForgetData, RunForgetErrors, RunForgetResponses, StopBackupData, StopBackupErrors, StopBackupResponses, TestConnectionData, TestConnectionResponses, UnmountVolumeData, UnmountVolumeResponses, UpdateBackupScheduleData, UpdateBackupScheduleResponses, UpdateVolumeData, UpdateVolumeErrors, UpdateVolumeResponses } from './types.gen'; -export type Options = Options2< - TData, - ThrowOnError -> & { - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; - /** - * You can pass arbitrary values through the `meta` object. This can be - * used to access values that aren't defined as part of the SDK function. - */ - meta?: Record; +export type Options = Options2 & { + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + /** + * You can pass arbitrary values through the `meta` object. This can be + * used to access values that aren't defined as part of the SDK function. + */ + meta?: Record; }; /** * Register a new user */ export const register = (options?: Options) => { - return (options?.client ?? client).post({ - url: "/api/v1/auth/register", - ...options, - headers: { - "Content-Type": "application/json", - ...options?.headers, - }, - }); + return (options?.client ?? client).post({ + url: '/api/v1/auth/register', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); }; /** * Login with username and password */ export const login = (options?: Options) => { - return (options?.client ?? client).post({ - url: "/api/v1/auth/login", - ...options, - headers: { - "Content-Type": "application/json", - ...options?.headers, - }, - }); + return (options?.client ?? client).post({ + url: '/api/v1/auth/login', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); }; /** * Logout current user */ export const logout = (options?: Options) => { - return (options?.client ?? client).post({ - url: "/api/v1/auth/logout", - ...options, - }); + return (options?.client ?? client).post({ + url: '/api/v1/auth/logout', + ...options + }); }; /** * Get current authenticated user */ export const getMe = (options?: Options) => { - return (options?.client ?? client).get({ - url: "/api/v1/auth/me", - ...options, - }); + return (options?.client ?? client).get({ + url: '/api/v1/auth/me', + ...options + }); }; /** * Get authentication system status */ export const getStatus = (options?: Options) => { - return (options?.client ?? client).get({ - url: "/api/v1/auth/status", - ...options, - }); + return (options?.client ?? client).get({ + url: '/api/v1/auth/status', + ...options + }); }; /** * Change current user password */ -export const changePassword = ( - options?: Options, -) => { - return (options?.client ?? client).post({ - url: "/api/v1/auth/change-password", - ...options, - headers: { - "Content-Type": "application/json", - ...options?.headers, - }, - }); +export const changePassword = (options?: Options) => { + return (options?.client ?? client).post({ + url: '/api/v1/auth/change-password', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); }; /** * List all volumes */ export const listVolumes = (options?: Options) => { - return (options?.client ?? client).get({ - url: "/api/v1/volumes", - ...options, - }); + return (options?.client ?? client).get({ + url: '/api/v1/volumes', + ...options + }); }; /** * Create a new volume */ -export const createVolume = ( - options?: Options, -) => { - return (options?.client ?? client).post({ - url: "/api/v1/volumes", - ...options, - headers: { - "Content-Type": "application/json", - ...options?.headers, - }, - }); +export const createVolume = (options?: Options) => { + return (options?.client ?? client).post({ + url: '/api/v1/volumes', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); }; /** * Test connection to backend */ -export const testConnection = ( - options?: Options, -) => { - return (options?.client ?? client).post({ - url: "/api/v1/volumes/test-connection", - ...options, - headers: { - "Content-Type": "application/json", - ...options?.headers, - }, - }); +export const testConnection = (options?: Options) => { + return (options?.client ?? client).post({ + url: '/api/v1/volumes/test-connection', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); }; /** * Delete a volume */ -export const deleteVolume = ( - options: Options, -) => { - return (options.client ?? client).delete({ - url: "/api/v1/volumes/{name}", - ...options, - }); +export const deleteVolume = (options: Options) => { + return (options.client ?? client).delete({ + url: '/api/v1/volumes/{name}', + ...options + }); }; /** * Get a volume by name */ export const getVolume = (options: Options) => { - return (options.client ?? client).get({ - url: "/api/v1/volumes/{name}", - ...options, - }); + return (options.client ?? client).get({ + url: '/api/v1/volumes/{name}', + ...options + }); }; /** * Update a volume's configuration */ -export const updateVolume = ( - options: Options, -) => { - return (options.client ?? client).put({ - url: "/api/v1/volumes/{name}", - ...options, - headers: { - "Content-Type": "application/json", - ...options.headers, - }, - }); +export const updateVolume = (options: Options) => { + return (options.client ?? client).put({ + url: '/api/v1/volumes/{name}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); }; /** * Get containers using a volume by name */ -export const getContainersUsingVolume = ( - options: Options, -) => { - return (options.client ?? client).get< - GetContainersUsingVolumeResponses, - GetContainersUsingVolumeErrors, - ThrowOnError - >({ - url: "/api/v1/volumes/{name}/containers", - ...options, - }); +export const getContainersUsingVolume = (options: Options) => { + return (options.client ?? client).get({ + url: '/api/v1/volumes/{name}/containers', + ...options + }); }; /** * Mount a volume */ export const mountVolume = (options: Options) => { - return (options.client ?? client).post({ - url: "/api/v1/volumes/{name}/mount", - ...options, - }); + return (options.client ?? client).post({ + url: '/api/v1/volumes/{name}/mount', + ...options + }); }; /** * Unmount a volume */ -export const unmountVolume = ( - options: Options, -) => { - return (options.client ?? client).post({ - url: "/api/v1/volumes/{name}/unmount", - ...options, - }); +export const unmountVolume = (options: Options) => { + return (options.client ?? client).post({ + url: '/api/v1/volumes/{name}/unmount', + ...options + }); }; /** * Perform a health check on a volume */ -export const healthCheckVolume = ( - options: Options, -) => { - return (options.client ?? client).post({ - url: "/api/v1/volumes/{name}/health-check", - ...options, - }); +export const healthCheckVolume = (options: Options) => { + return (options.client ?? client).post({ + url: '/api/v1/volumes/{name}/health-check', + ...options + }); }; /** * List files in a volume directory */ export const listFiles = (options: Options) => { - return (options.client ?? client).get({ - url: "/api/v1/volumes/{name}/files", - ...options, - }); + return (options.client ?? client).get({ + url: '/api/v1/volumes/{name}/files', + ...options + }); }; /** * Browse directories on the host filesystem */ -export const browseFilesystem = ( - options?: Options, -) => { - return (options?.client ?? client).get({ - url: "/api/v1/volumes/filesystem/browse", - ...options, - }); +export const browseFilesystem = (options?: Options) => { + return (options?.client ?? client).get({ + url: '/api/v1/volumes/filesystem/browse', + ...options + }); }; /** * List all repositories */ -export const listRepositories = ( - options?: Options, -) => { - return (options?.client ?? client).get({ - url: "/api/v1/repositories", - ...options, - }); +export const listRepositories = (options?: Options) => { + return (options?.client ?? client).get({ + url: '/api/v1/repositories', + ...options + }); }; /** * Create a new restic repository */ -export const createRepository = ( - options?: Options, -) => { - return (options?.client ?? client).post({ - url: "/api/v1/repositories", - ...options, - headers: { - "Content-Type": "application/json", - ...options?.headers, - }, - }); +export const createRepository = (options?: Options) => { + return (options?.client ?? client).post({ + url: '/api/v1/repositories', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); }; /** * List all configured rclone remotes on the host system */ -export const listRcloneRemotes = ( - options?: Options, -) => { - return (options?.client ?? client).get({ - url: "/api/v1/repositories/rclone-remotes", - ...options, - }); +export const listRcloneRemotes = (options?: Options) => { + return (options?.client ?? client).get({ + url: '/api/v1/repositories/rclone-remotes', + ...options + }); }; /** * Delete a repository */ -export const deleteRepository = ( - options: Options, -) => { - return (options.client ?? client).delete({ - url: "/api/v1/repositories/{name}", - ...options, - }); +export const deleteRepository = (options: Options) => { + return (options.client ?? client).delete({ + url: '/api/v1/repositories/{name}', + ...options + }); }; /** * Get a single repository by name */ -export const getRepository = ( - options: Options, -) => { - return (options.client ?? client).get({ - url: "/api/v1/repositories/{name}", - ...options, - }); +export const getRepository = (options: Options) => { + return (options.client ?? client).get({ + url: '/api/v1/repositories/{name}', + ...options + }); }; /** * List all snapshots in a repository */ -export const listSnapshots = ( - options: Options, -) => { - return (options.client ?? client).get({ - url: "/api/v1/repositories/{name}/snapshots", - ...options, - }); +export const listSnapshots = (options: Options) => { + return (options.client ?? client).get({ + url: '/api/v1/repositories/{name}/snapshots', + ...options + }); }; /** * Get details of a specific snapshot */ -export const getSnapshotDetails = ( - options: Options, -) => { - return (options.client ?? client).get({ - url: "/api/v1/repositories/{name}/snapshots/{snapshotId}", - ...options, - }); +export const getSnapshotDetails = (options: Options) => { + return (options.client ?? client).get({ + url: '/api/v1/repositories/{name}/snapshots/{snapshotId}', + ...options + }); }; /** * List files and directories in a snapshot */ -export const listSnapshotFiles = ( - options: Options, -) => { - return (options.client ?? client).get({ - url: "/api/v1/repositories/{name}/snapshots/{snapshotId}/files", - ...options, - }); +export const listSnapshotFiles = (options: Options) => { + return (options.client ?? client).get({ + url: '/api/v1/repositories/{name}/snapshots/{snapshotId}/files', + ...options + }); }; /** * Restore a snapshot to a target path on the filesystem */ -export const restoreSnapshot = ( - options: Options, -) => { - return (options.client ?? client).post({ - url: "/api/v1/repositories/{name}/restore", - ...options, - headers: { - "Content-Type": "application/json", - ...options.headers, - }, - }); +export const restoreSnapshot = (options: Options) => { + return (options.client ?? client).post({ + url: '/api/v1/repositories/{name}/restore', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); }; /** * Run doctor operations on a repository to fix common issues (unlock, check, repair index). Use this when the repository is locked or has errors. */ -export const doctorRepository = ( - options: Options, -) => { - return (options.client ?? client).post({ - url: "/api/v1/repositories/{name}/doctor", - ...options, - }); +export const doctorRepository = (options: Options) => { + return (options.client ?? client).post({ + url: '/api/v1/repositories/{name}/doctor', + ...options + }); }; /** * List all backup schedules */ -export const listBackupSchedules = ( - options?: Options, -) => { - return (options?.client ?? client).get({ - url: "/api/v1/backups", - ...options, - }); +export const listBackupSchedules = (options?: Options) => { + return (options?.client ?? client).get({ + url: '/api/v1/backups', + ...options + }); }; /** * Create a new backup schedule for a volume */ -export const createBackupSchedule = ( - options?: Options, -) => { - return (options?.client ?? client).post({ - url: "/api/v1/backups", - ...options, - headers: { - "Content-Type": "application/json", - ...options?.headers, - }, - }); +export const createBackupSchedule = (options?: Options) => { + return (options?.client ?? client).post({ + url: '/api/v1/backups', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); }; /** * Delete a backup schedule */ -export const deleteBackupSchedule = ( - options: Options, -) => { - return (options.client ?? client).delete({ - url: "/api/v1/backups/{scheduleId}", - ...options, - }); +export const deleteBackupSchedule = (options: Options) => { + return (options.client ?? client).delete({ + url: '/api/v1/backups/{scheduleId}', + ...options + }); }; /** * Get a backup schedule by ID */ -export const getBackupSchedule = ( - options: Options, -) => { - return (options.client ?? client).get({ - url: "/api/v1/backups/{scheduleId}", - ...options, - }); +export const getBackupSchedule = (options: Options) => { + return (options.client ?? client).get({ + url: '/api/v1/backups/{scheduleId}', + ...options + }); }; /** * Update a backup schedule */ -export const updateBackupSchedule = ( - options: Options, -) => { - return (options.client ?? client).patch({ - url: "/api/v1/backups/{scheduleId}", - ...options, - headers: { - "Content-Type": "application/json", - ...options.headers, - }, - }); +export const updateBackupSchedule = (options: Options) => { + return (options.client ?? client).patch({ + url: '/api/v1/backups/{scheduleId}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); }; /** * Get a backup schedule for a specific volume */ -export const getBackupScheduleForVolume = ( - options: Options, -) => { - return (options.client ?? client).get({ - url: "/api/v1/backups/volume/{volumeId}", - ...options, - }); +export const getBackupScheduleForVolume = (options: Options) => { + return (options.client ?? client).get({ + url: '/api/v1/backups/volume/{volumeId}', + ...options + }); }; /** * Trigger a backup immediately for a schedule */ -export const runBackupNow = ( - options: Options, -) => { - return (options.client ?? client).post({ - url: "/api/v1/backups/{scheduleId}/run", - ...options, - }); +export const runBackupNow = (options: Options) => { + return (options.client ?? client).post({ + url: '/api/v1/backups/{scheduleId}/run', + ...options + }); }; /** * Stop a backup that is currently in progress */ export const stopBackup = (options: Options) => { - return (options.client ?? client).post({ - url: "/api/v1/backups/{scheduleId}/stop", - ...options, - }); + return (options.client ?? client).post({ + url: '/api/v1/backups/{scheduleId}/stop', + ...options + }); +}; + +/** + * Manually apply retention policy to clean up old snapshots + */ +export const runForget = (options: Options) => { + return (options.client ?? client).post({ + url: '/api/v1/backups/{scheduleId}/forget', + ...options + }); }; /** * Get system information including available capabilities */ -export const getSystemInfo = ( - options?: Options, -) => { - return (options?.client ?? client).get({ - url: "/api/v1/system/info", - ...options, - }); +export const getSystemInfo = (options?: Options) => { + return (options?.client ?? client).get({ + url: '/api/v1/system/info', + ...options + }); }; /** * Download the Restic password file for backup recovery. Requires password re-authentication. */ -export const downloadResticPassword = ( - options?: Options, -) => { - return (options?.client ?? client).post({ - url: "/api/v1/system/restic-password", - ...options, - headers: { - "Content-Type": "application/json", - ...options?.headers, - }, - }); +export const downloadResticPassword = (options?: Options) => { + return (options?.client ?? client).post({ + url: '/api/v1/system/restic-password', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); }; diff --git a/app/client/api-client/types.gen.ts b/app/client/api-client/types.gen.ts index 2f5bca8..db35bf8 100644 --- a/app/client/api-client/types.gen.ts +++ b/app/client/api-client/types.gen.ts @@ -1,1678 +1,1669 @@ // This file is auto-generated by @hey-api/openapi-ts export type ClientOptions = { - baseUrl: "http://192.168.2.42:4096" | (string & {}); + baseUrl: 'http://192.168.2.42:4096' | (string & {}); }; export type RegisterData = { - body?: { - password: string; - username: string; - }; - path?: never; - query?: never; - url: "/api/v1/auth/register"; + body?: { + password: string; + username: string; + }; + path?: never; + query?: never; + url: '/api/v1/auth/register'; }; export type RegisterResponses = { - /** - * User created successfully - */ - 201: { - message: string; - success: boolean; - user?: { - hasDownloadedResticPassword: boolean; - id: number; - username: string; - }; - }; + /** + * User created successfully + */ + 201: { + message: string; + success: boolean; + user?: { + hasDownloadedResticPassword: boolean; + id: number; + username: string; + }; + }; }; export type RegisterResponse = RegisterResponses[keyof RegisterResponses]; export type LoginData = { - body?: { - password: string; - username: string; - }; - path?: never; - query?: never; - url: "/api/v1/auth/login"; + body?: { + password: string; + username: string; + }; + path?: never; + query?: never; + url: '/api/v1/auth/login'; }; export type LoginResponses = { - /** - * Login successful - */ - 200: { - message: string; - success: boolean; - user?: { - hasDownloadedResticPassword: boolean; - id: number; - username: string; - }; - }; + /** + * Login successful + */ + 200: { + message: string; + success: boolean; + user?: { + hasDownloadedResticPassword: boolean; + id: number; + username: string; + }; + }; }; export type LoginResponse = LoginResponses[keyof LoginResponses]; export type LogoutData = { - body?: never; - path?: never; - query?: never; - url: "/api/v1/auth/logout"; + body?: never; + path?: never; + query?: never; + url: '/api/v1/auth/logout'; }; export type LogoutResponses = { - /** - * Logout successful - */ - 200: { - success: boolean; - }; + /** + * Logout successful + */ + 200: { + success: boolean; + }; }; export type LogoutResponse = LogoutResponses[keyof LogoutResponses]; export type GetMeData = { - body?: never; - path?: never; - query?: never; - url: "/api/v1/auth/me"; + body?: never; + path?: never; + query?: never; + url: '/api/v1/auth/me'; }; export type GetMeResponses = { - /** - * Current user information - */ - 200: { - message: string; - success: boolean; - user?: { - hasDownloadedResticPassword: boolean; - id: number; - username: string; - }; - }; + /** + * Current user information + */ + 200: { + message: string; + success: boolean; + user?: { + hasDownloadedResticPassword: boolean; + id: number; + username: string; + }; + }; }; export type GetMeResponse = GetMeResponses[keyof GetMeResponses]; export type GetStatusData = { - body?: never; - path?: never; - query?: never; - url: "/api/v1/auth/status"; + body?: never; + path?: never; + query?: never; + url: '/api/v1/auth/status'; }; export type GetStatusResponses = { - /** - * Authentication system status - */ - 200: { - hasUsers: boolean; - }; + /** + * Authentication system status + */ + 200: { + hasUsers: boolean; + }; }; export type GetStatusResponse = GetStatusResponses[keyof GetStatusResponses]; export type ChangePasswordData = { - body?: { - currentPassword: string; - newPassword: string; - }; - path?: never; - query?: never; - url: "/api/v1/auth/change-password"; + body?: { + currentPassword: string; + newPassword: string; + }; + path?: never; + query?: never; + url: '/api/v1/auth/change-password'; }; export type ChangePasswordResponses = { - /** - * Password changed successfully - */ - 200: { - message: string; - success: boolean; - }; + /** + * Password changed successfully + */ + 200: { + message: string; + success: boolean; + }; }; export type ChangePasswordResponse = ChangePasswordResponses[keyof ChangePasswordResponses]; export type ListVolumesData = { - body?: never; - path?: never; - query?: never; - url: "/api/v1/volumes"; + body?: never; + path?: never; + query?: never; + url: '/api/v1/volumes'; }; export type ListVolumesResponses = { - /** - * A list of volumes - */ - 200: Array<{ - autoRemount: boolean; - config: - | { - backend: "directory"; - path: string; - readOnly?: false; - } - | { - backend: "nfs"; - exportPath: string; - server: string; - version: "3" | "4" | "4.1"; - port?: number; - readOnly?: boolean; - } - | { - backend: "smb"; - password: string; - server: string; - share: string; - username: string; - vers?: "1.0" | "2.0" | "2.1" | "3.0"; - port?: number; - domain?: string; - readOnly?: boolean; - } - | { - backend: "webdav"; - path: string; - server: string; - port?: number; - password?: string; - readOnly?: boolean; - ssl?: boolean; - username?: string; - }; - createdAt: number; - id: number; - lastError: string | null; - lastHealthCheck: number; - name: string; - status: "error" | "mounted" | "unmounted"; - type: "directory" | "nfs" | "smb" | "webdav"; - updatedAt: number; - }>; + /** + * A list of volumes + */ + 200: Array<{ + autoRemount: boolean; + config: { + backend: 'directory'; + path: string; + readOnly?: false; + } | { + backend: 'nfs'; + exportPath: string; + server: string; + version: '3' | '4' | '4.1'; + port?: number; + readOnly?: boolean; + } | { + backend: 'smb'; + password: string; + server: string; + share: string; + username: string; + vers?: '1.0' | '2.0' | '2.1' | '3.0'; + port?: number; + domain?: string; + readOnly?: boolean; + } | { + backend: 'webdav'; + path: string; + server: string; + port?: number; + password?: string; + readOnly?: boolean; + ssl?: boolean; + username?: string; + }; + createdAt: number; + id: number; + lastError: string | null; + lastHealthCheck: number; + name: string; + status: 'error' | 'mounted' | 'unmounted'; + type: 'directory' | 'nfs' | 'smb' | 'webdav'; + updatedAt: number; + }>; }; export type ListVolumesResponse = ListVolumesResponses[keyof ListVolumesResponses]; export type CreateVolumeData = { - body?: { - config: - | { - backend: "directory"; - path: string; - readOnly?: false; - } - | { - backend: "nfs"; - exportPath: string; - server: string; - version: "3" | "4" | "4.1"; - port?: number; - readOnly?: boolean; - } - | { - backend: "smb"; - password: string; - server: string; - share: string; - username: string; - vers?: "1.0" | "2.0" | "2.1" | "3.0"; - port?: number; - domain?: string; - readOnly?: boolean; - } - | { - backend: "webdav"; - path: string; - server: string; - port?: number; - password?: string; - readOnly?: boolean; - ssl?: boolean; - username?: string; - }; - name: string; - }; - path?: never; - query?: never; - url: "/api/v1/volumes"; + body?: { + config: { + backend: 'directory'; + path: string; + readOnly?: false; + } | { + backend: 'nfs'; + exportPath: string; + server: string; + version: '3' | '4' | '4.1'; + port?: number; + readOnly?: boolean; + } | { + backend: 'smb'; + password: string; + server: string; + share: string; + username: string; + vers?: '1.0' | '2.0' | '2.1' | '3.0'; + port?: number; + domain?: string; + readOnly?: boolean; + } | { + backend: 'webdav'; + path: string; + server: string; + port?: number; + password?: string; + readOnly?: boolean; + ssl?: boolean; + username?: string; + }; + name: string; + }; + path?: never; + query?: never; + url: '/api/v1/volumes'; }; export type CreateVolumeResponses = { - /** - * Volume created successfully - */ - 201: { - autoRemount: boolean; - config: - | { - backend: "directory"; - path: string; - readOnly?: false; - } - | { - backend: "nfs"; - exportPath: string; - server: string; - version: "3" | "4" | "4.1"; - port?: number; - readOnly?: boolean; - } - | { - backend: "smb"; - password: string; - server: string; - share: string; - username: string; - vers?: "1.0" | "2.0" | "2.1" | "3.0"; - port?: number; - domain?: string; - readOnly?: boolean; - } - | { - backend: "webdav"; - path: string; - server: string; - port?: number; - password?: string; - readOnly?: boolean; - ssl?: boolean; - username?: string; - }; - createdAt: number; - id: number; - lastError: string | null; - lastHealthCheck: number; - name: string; - status: "error" | "mounted" | "unmounted"; - type: "directory" | "nfs" | "smb" | "webdav"; - updatedAt: number; - }; + /** + * Volume created successfully + */ + 201: { + autoRemount: boolean; + config: { + backend: 'directory'; + path: string; + readOnly?: false; + } | { + backend: 'nfs'; + exportPath: string; + server: string; + version: '3' | '4' | '4.1'; + port?: number; + readOnly?: boolean; + } | { + backend: 'smb'; + password: string; + server: string; + share: string; + username: string; + vers?: '1.0' | '2.0' | '2.1' | '3.0'; + port?: number; + domain?: string; + readOnly?: boolean; + } | { + backend: 'webdav'; + path: string; + server: string; + port?: number; + password?: string; + readOnly?: boolean; + ssl?: boolean; + username?: string; + }; + createdAt: number; + id: number; + lastError: string | null; + lastHealthCheck: number; + name: string; + status: 'error' | 'mounted' | 'unmounted'; + type: 'directory' | 'nfs' | 'smb' | 'webdav'; + updatedAt: number; + }; }; export type CreateVolumeResponse = CreateVolumeResponses[keyof CreateVolumeResponses]; export type TestConnectionData = { - body?: { - config: - | { - backend: "directory"; - path: string; - readOnly?: false; - } - | { - backend: "nfs"; - exportPath: string; - server: string; - version: "3" | "4" | "4.1"; - port?: number; - readOnly?: boolean; - } - | { - backend: "smb"; - password: string; - server: string; - share: string; - username: string; - vers?: "1.0" | "2.0" | "2.1" | "3.0"; - port?: number; - domain?: string; - readOnly?: boolean; - } - | { - backend: "webdav"; - path: string; - server: string; - port?: number; - password?: string; - readOnly?: boolean; - ssl?: boolean; - username?: string; - }; - }; - path?: never; - query?: never; - url: "/api/v1/volumes/test-connection"; + body?: { + config: { + backend: 'directory'; + path: string; + readOnly?: false; + } | { + backend: 'nfs'; + exportPath: string; + server: string; + version: '3' | '4' | '4.1'; + port?: number; + readOnly?: boolean; + } | { + backend: 'smb'; + password: string; + server: string; + share: string; + username: string; + vers?: '1.0' | '2.0' | '2.1' | '3.0'; + port?: number; + domain?: string; + readOnly?: boolean; + } | { + backend: 'webdav'; + path: string; + server: string; + port?: number; + password?: string; + readOnly?: boolean; + ssl?: boolean; + username?: string; + }; + }; + path?: never; + query?: never; + url: '/api/v1/volumes/test-connection'; }; export type TestConnectionResponses = { - /** - * Connection test result - */ - 200: { - message: string; - success: boolean; - }; + /** + * Connection test result + */ + 200: { + message: string; + success: boolean; + }; }; export type TestConnectionResponse = TestConnectionResponses[keyof TestConnectionResponses]; export type DeleteVolumeData = { - body?: never; - path: { - name: string; - }; - query?: never; - url: "/api/v1/volumes/{name}"; + body?: never; + path: { + name: string; + }; + query?: never; + url: '/api/v1/volumes/{name}'; }; export type DeleteVolumeResponses = { - /** - * Volume deleted successfully - */ - 200: { - message: string; - }; + /** + * Volume deleted successfully + */ + 200: { + message: string; + }; }; export type DeleteVolumeResponse = DeleteVolumeResponses[keyof DeleteVolumeResponses]; export type GetVolumeData = { - body?: never; - path: { - name: string; - }; - query?: never; - url: "/api/v1/volumes/{name}"; + body?: never; + path: { + name: string; + }; + query?: never; + url: '/api/v1/volumes/{name}'; }; export type GetVolumeErrors = { - /** - * Volume not found - */ - 404: unknown; + /** + * Volume not found + */ + 404: unknown; }; export type GetVolumeResponses = { - /** - * Volume details - */ - 200: { - statfs: { - free: number; - total: number; - used: number; - }; - volume: { - autoRemount: boolean; - config: - | { - backend: "directory"; - path: string; - readOnly?: false; - } - | { - backend: "nfs"; - exportPath: string; - server: string; - version: "3" | "4" | "4.1"; - port?: number; - readOnly?: boolean; - } - | { - backend: "smb"; - password: string; - server: string; - share: string; - username: string; - vers?: "1.0" | "2.0" | "2.1" | "3.0"; - port?: number; - domain?: string; - readOnly?: boolean; - } - | { - backend: "webdav"; - path: string; - server: string; - port?: number; - password?: string; - readOnly?: boolean; - ssl?: boolean; - username?: string; - }; - createdAt: number; - id: number; - lastError: string | null; - lastHealthCheck: number; - name: string; - status: "error" | "mounted" | "unmounted"; - type: "directory" | "nfs" | "smb" | "webdav"; - updatedAt: number; - }; - }; + /** + * Volume details + */ + 200: { + statfs: { + free: number; + total: number; + used: number; + }; + volume: { + autoRemount: boolean; + config: { + backend: 'directory'; + path: string; + readOnly?: false; + } | { + backend: 'nfs'; + exportPath: string; + server: string; + version: '3' | '4' | '4.1'; + port?: number; + readOnly?: boolean; + } | { + backend: 'smb'; + password: string; + server: string; + share: string; + username: string; + vers?: '1.0' | '2.0' | '2.1' | '3.0'; + port?: number; + domain?: string; + readOnly?: boolean; + } | { + backend: 'webdav'; + path: string; + server: string; + port?: number; + password?: string; + readOnly?: boolean; + ssl?: boolean; + username?: string; + }; + createdAt: number; + id: number; + lastError: string | null; + lastHealthCheck: number; + name: string; + status: 'error' | 'mounted' | 'unmounted'; + type: 'directory' | 'nfs' | 'smb' | 'webdav'; + updatedAt: number; + }; + }; }; export type GetVolumeResponse = GetVolumeResponses[keyof GetVolumeResponses]; export type UpdateVolumeData = { - body?: { - autoRemount?: boolean; - config?: - | { - backend: "directory"; - path: string; - readOnly?: false; - } - | { - backend: "nfs"; - exportPath: string; - server: string; - version: "3" | "4" | "4.1"; - port?: number; - readOnly?: boolean; - } - | { - backend: "smb"; - password: string; - server: string; - share: string; - username: string; - vers?: "1.0" | "2.0" | "2.1" | "3.0"; - port?: number; - domain?: string; - readOnly?: boolean; - } - | { - backend: "webdav"; - path: string; - server: string; - port?: number; - password?: string; - readOnly?: boolean; - ssl?: boolean; - username?: string; - }; - }; - path: { - name: string; - }; - query?: never; - url: "/api/v1/volumes/{name}"; + body?: { + autoRemount?: boolean; + config?: { + backend: 'directory'; + path: string; + readOnly?: false; + } | { + backend: 'nfs'; + exportPath: string; + server: string; + version: '3' | '4' | '4.1'; + port?: number; + readOnly?: boolean; + } | { + backend: 'smb'; + password: string; + server: string; + share: string; + username: string; + vers?: '1.0' | '2.0' | '2.1' | '3.0'; + port?: number; + domain?: string; + readOnly?: boolean; + } | { + backend: 'webdav'; + path: string; + server: string; + port?: number; + password?: string; + readOnly?: boolean; + ssl?: boolean; + username?: string; + }; + }; + path: { + name: string; + }; + query?: never; + url: '/api/v1/volumes/{name}'; }; export type UpdateVolumeErrors = { - /** - * Volume not found - */ - 404: unknown; + /** + * Volume not found + */ + 404: unknown; }; export type UpdateVolumeResponses = { - /** - * Volume updated successfully - */ - 200: { - autoRemount: boolean; - config: - | { - backend: "directory"; - path: string; - readOnly?: false; - } - | { - backend: "nfs"; - exportPath: string; - server: string; - version: "3" | "4" | "4.1"; - port?: number; - readOnly?: boolean; - } - | { - backend: "smb"; - password: string; - server: string; - share: string; - username: string; - vers?: "1.0" | "2.0" | "2.1" | "3.0"; - port?: number; - domain?: string; - readOnly?: boolean; - } - | { - backend: "webdav"; - path: string; - server: string; - port?: number; - password?: string; - readOnly?: boolean; - ssl?: boolean; - username?: string; - }; - createdAt: number; - id: number; - lastError: string | null; - lastHealthCheck: number; - name: string; - status: "error" | "mounted" | "unmounted"; - type: "directory" | "nfs" | "smb" | "webdav"; - updatedAt: number; - }; + /** + * Volume updated successfully + */ + 200: { + autoRemount: boolean; + config: { + backend: 'directory'; + path: string; + readOnly?: false; + } | { + backend: 'nfs'; + exportPath: string; + server: string; + version: '3' | '4' | '4.1'; + port?: number; + readOnly?: boolean; + } | { + backend: 'smb'; + password: string; + server: string; + share: string; + username: string; + vers?: '1.0' | '2.0' | '2.1' | '3.0'; + port?: number; + domain?: string; + readOnly?: boolean; + } | { + backend: 'webdav'; + path: string; + server: string; + port?: number; + password?: string; + readOnly?: boolean; + ssl?: boolean; + username?: string; + }; + createdAt: number; + id: number; + lastError: string | null; + lastHealthCheck: number; + name: string; + status: 'error' | 'mounted' | 'unmounted'; + type: 'directory' | 'nfs' | 'smb' | 'webdav'; + updatedAt: number; + }; }; export type UpdateVolumeResponse = UpdateVolumeResponses[keyof UpdateVolumeResponses]; export type GetContainersUsingVolumeData = { - body?: never; - path: { - name: string; - }; - query?: never; - url: "/api/v1/volumes/{name}/containers"; + body?: never; + path: { + name: string; + }; + query?: never; + url: '/api/v1/volumes/{name}/containers'; }; export type GetContainersUsingVolumeErrors = { - /** - * Volume not found - */ - 404: unknown; + /** + * Volume not found + */ + 404: unknown; }; export type GetContainersUsingVolumeResponses = { - /** - * List of containers using the volume - */ - 200: Array<{ - id: string; - image: string; - name: string; - state: string; - }>; + /** + * List of containers using the volume + */ + 200: Array<{ + id: string; + image: string; + name: string; + state: string; + }>; }; -export type GetContainersUsingVolumeResponse = - GetContainersUsingVolumeResponses[keyof GetContainersUsingVolumeResponses]; +export type GetContainersUsingVolumeResponse = GetContainersUsingVolumeResponses[keyof GetContainersUsingVolumeResponses]; export type MountVolumeData = { - body?: never; - path: { - name: string; - }; - query?: never; - url: "/api/v1/volumes/{name}/mount"; + body?: never; + path: { + name: string; + }; + query?: never; + url: '/api/v1/volumes/{name}/mount'; }; export type MountVolumeResponses = { - /** - * Volume mounted successfully - */ - 200: { - status: "error" | "mounted" | "unmounted"; - error?: string; - }; + /** + * Volume mounted successfully + */ + 200: { + status: 'error' | 'mounted' | 'unmounted'; + error?: string; + }; }; export type MountVolumeResponse = MountVolumeResponses[keyof MountVolumeResponses]; export type UnmountVolumeData = { - body?: never; - path: { - name: string; - }; - query?: never; - url: "/api/v1/volumes/{name}/unmount"; + body?: never; + path: { + name: string; + }; + query?: never; + url: '/api/v1/volumes/{name}/unmount'; }; export type UnmountVolumeResponses = { - /** - * Volume unmounted successfully - */ - 200: { - status: "error" | "mounted" | "unmounted"; - error?: string; - }; + /** + * Volume unmounted successfully + */ + 200: { + status: 'error' | 'mounted' | 'unmounted'; + error?: string; + }; }; export type UnmountVolumeResponse = UnmountVolumeResponses[keyof UnmountVolumeResponses]; export type HealthCheckVolumeData = { - body?: never; - path: { - name: string; - }; - query?: never; - url: "/api/v1/volumes/{name}/health-check"; + body?: never; + path: { + name: string; + }; + query?: never; + url: '/api/v1/volumes/{name}/health-check'; }; export type HealthCheckVolumeErrors = { - /** - * Volume not found - */ - 404: unknown; + /** + * Volume not found + */ + 404: unknown; }; export type HealthCheckVolumeResponses = { - /** - * Volume health check result - */ - 200: { - status: "error" | "mounted" | "unmounted"; - error?: string; - }; + /** + * Volume health check result + */ + 200: { + status: 'error' | 'mounted' | 'unmounted'; + error?: string; + }; }; export type HealthCheckVolumeResponse = HealthCheckVolumeResponses[keyof HealthCheckVolumeResponses]; export type ListFilesData = { - body?: never; - path: { - name: string; - }; - query?: { - /** - * Subdirectory path to list (relative to volume root) - */ - path?: string; - }; - url: "/api/v1/volumes/{name}/files"; + body?: never; + path: { + name: string; + }; + query?: { + /** + * Subdirectory path to list (relative to volume root) + */ + path?: string; + }; + url: '/api/v1/volumes/{name}/files'; }; export type ListFilesResponses = { - /** - * List of files in the volume - */ - 200: { - files: Array<{ - name: string; - path: string; - type: "directory" | "file"; - modifiedAt?: number; - size?: number; - }>; - path: string; - }; + /** + * List of files in the volume + */ + 200: { + files: Array<{ + name: string; + path: string; + type: 'directory' | 'file'; + modifiedAt?: number; + size?: number; + }>; + path: string; + }; }; export type ListFilesResponse = ListFilesResponses[keyof ListFilesResponses]; export type BrowseFilesystemData = { - body?: never; - path?: never; - query?: { - /** - * Directory path to browse (absolute path, defaults to /) - */ - path?: string; - }; - url: "/api/v1/volumes/filesystem/browse"; + body?: never; + path?: never; + query?: { + /** + * Directory path to browse (absolute path, defaults to /) + */ + path?: string; + }; + url: '/api/v1/volumes/filesystem/browse'; }; export type BrowseFilesystemResponses = { - /** - * List of directories in the specified path - */ - 200: { - directories: Array<{ - name: string; - path: string; - type: "directory" | "file"; - modifiedAt?: number; - size?: number; - }>; - path: string; - }; + /** + * List of directories in the specified path + */ + 200: { + directories: Array<{ + name: string; + path: string; + type: 'directory' | 'file'; + modifiedAt?: number; + size?: number; + }>; + path: string; + }; }; export type BrowseFilesystemResponse = BrowseFilesystemResponses[keyof BrowseFilesystemResponses]; export type ListRepositoriesData = { - body?: never; - path?: never; - query?: never; - url: "/api/v1/repositories"; + body?: never; + path?: never; + query?: never; + url: '/api/v1/repositories'; }; export type ListRepositoriesResponses = { - /** - * List of repositories - */ - 200: Array<{ - compressionMode: "auto" | "better" | "fastest" | "max" | "off" | null; - config: - | { - accessKeyId: string; - backend: "s3"; - bucket: string; - endpoint: string; - secretAccessKey: string; - } - | { - accountKey: string; - accountName: string; - backend: "azure"; - container: string; - endpointSuffix?: string; - } - | { - backend: "gcs"; - bucket: string; - credentialsJson: string; - projectId: string; - } - | { - backend: "local"; - name: string; - } - | { - backend: "rclone"; - path: string; - remote: string; - }; - createdAt: number; - id: string; - lastChecked: number | null; - lastError: string | null; - name: string; - status: "error" | "healthy" | "unknown" | null; - type: "azure" | "gcs" | "local" | "rclone" | "s3"; - updatedAt: number; - }>; + /** + * List of repositories + */ + 200: Array<{ + compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null; + config: { + accessKeyId: string; + backend: 'r2'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accessKeyId: string; + backend: 's3'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accountKey: string; + accountName: string; + backend: 'azure'; + container: string; + endpointSuffix?: string; + } | { + backend: 'gcs'; + bucket: string; + credentialsJson: string; + projectId: string; + } | { + backend: 'local'; + name: string; + } | { + backend: 'rclone'; + path: string; + remote: string; + }; + createdAt: number; + id: string; + lastChecked: number | null; + lastError: string | null; + name: string; + status: 'error' | 'healthy' | 'unknown' | null; + type: 'azure' | 'gcs' | 'local' | 'r2' | 'rclone' | 's3'; + updatedAt: number; + }>; }; export type ListRepositoriesResponse = ListRepositoriesResponses[keyof ListRepositoriesResponses]; export type CreateRepositoryData = { - body?: { - config: - | { - accessKeyId: string; - backend: "s3"; - bucket: string; - endpoint: string; - secretAccessKey: string; - } - | { - accountKey: string; - accountName: string; - backend: "azure"; - container: string; - endpointSuffix?: string; - } - | { - backend: "gcs"; - bucket: string; - credentialsJson: string; - projectId: string; - } - | { - backend: "local"; - name: string; - } - | { - backend: "rclone"; - path: string; - remote: string; - }; - name: string; - compressionMode?: "auto" | "better" | "fastest" | "max" | "off"; - }; - path?: never; - query?: never; - url: "/api/v1/repositories"; + body?: { + config: { + accessKeyId: string; + backend: 'r2'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accessKeyId: string; + backend: 's3'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accountKey: string; + accountName: string; + backend: 'azure'; + container: string; + endpointSuffix?: string; + } | { + backend: 'gcs'; + bucket: string; + credentialsJson: string; + projectId: string; + } | { + backend: 'local'; + name: string; + } | { + backend: 'rclone'; + path: string; + remote: string; + }; + name: string; + compressionMode?: 'auto' | 'better' | 'fastest' | 'max' | 'off'; + }; + path?: never; + query?: never; + url: '/api/v1/repositories'; }; export type CreateRepositoryResponses = { - /** - * Repository created successfully - */ - 201: { - message: string; - repository: { - id: string; - name: string; - }; - }; + /** + * Repository created successfully + */ + 201: { + message: string; + repository: { + id: string; + name: string; + }; + }; }; export type CreateRepositoryResponse = CreateRepositoryResponses[keyof CreateRepositoryResponses]; export type ListRcloneRemotesData = { - body?: never; - path?: never; - query?: never; - url: "/api/v1/repositories/rclone-remotes"; + body?: never; + path?: never; + query?: never; + url: '/api/v1/repositories/rclone-remotes'; }; export type ListRcloneRemotesResponses = { - /** - * List of rclone remotes - */ - 200: Array<{ - name: string; - type: string; - }>; + /** + * List of rclone remotes + */ + 200: Array<{ + name: string; + type: string; + }>; }; export type ListRcloneRemotesResponse = ListRcloneRemotesResponses[keyof ListRcloneRemotesResponses]; export type DeleteRepositoryData = { - body?: never; - path: { - name: string; - }; - query?: never; - url: "/api/v1/repositories/{name}"; + body?: never; + path: { + name: string; + }; + query?: never; + url: '/api/v1/repositories/{name}'; }; export type DeleteRepositoryResponses = { - /** - * Repository deleted successfully - */ - 200: { - message: string; - }; + /** + * Repository deleted successfully + */ + 200: { + message: string; + }; }; export type DeleteRepositoryResponse = DeleteRepositoryResponses[keyof DeleteRepositoryResponses]; export type GetRepositoryData = { - body?: never; - path: { - name: string; - }; - query?: never; - url: "/api/v1/repositories/{name}"; + body?: never; + path: { + name: string; + }; + query?: never; + url: '/api/v1/repositories/{name}'; }; export type GetRepositoryResponses = { - /** - * Repository details - */ - 200: { - compressionMode: "auto" | "better" | "fastest" | "max" | "off" | null; - config: - | { - accessKeyId: string; - backend: "s3"; - bucket: string; - endpoint: string; - secretAccessKey: string; - } - | { - accountKey: string; - accountName: string; - backend: "azure"; - container: string; - endpointSuffix?: string; - } - | { - backend: "gcs"; - bucket: string; - credentialsJson: string; - projectId: string; - } - | { - backend: "local"; - name: string; - } - | { - backend: "rclone"; - path: string; - remote: string; - }; - createdAt: number; - id: string; - lastChecked: number | null; - lastError: string | null; - name: string; - status: "error" | "healthy" | "unknown" | null; - type: "azure" | "gcs" | "local" | "rclone" | "s3"; - updatedAt: number; - }; + /** + * Repository details + */ + 200: { + compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null; + config: { + accessKeyId: string; + backend: 'r2'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accessKeyId: string; + backend: 's3'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accountKey: string; + accountName: string; + backend: 'azure'; + container: string; + endpointSuffix?: string; + } | { + backend: 'gcs'; + bucket: string; + credentialsJson: string; + projectId: string; + } | { + backend: 'local'; + name: string; + } | { + backend: 'rclone'; + path: string; + remote: string; + }; + createdAt: number; + id: string; + lastChecked: number | null; + lastError: string | null; + name: string; + status: 'error' | 'healthy' | 'unknown' | null; + type: 'azure' | 'gcs' | 'local' | 'r2' | 'rclone' | 's3'; + updatedAt: number; + }; }; export type GetRepositoryResponse = GetRepositoryResponses[keyof GetRepositoryResponses]; export type ListSnapshotsData = { - body?: never; - path: { - name: string; - }; - query?: { - backupId?: string; - }; - url: "/api/v1/repositories/{name}/snapshots"; + body?: never; + path: { + name: string; + }; + query?: { + backupId?: string; + }; + url: '/api/v1/repositories/{name}/snapshots'; }; export type ListSnapshotsResponses = { - /** - * List of snapshots - */ - 200: Array<{ - duration: number; - paths: Array; - short_id: string; - size: number; - time: number; - }>; + /** + * List of snapshots + */ + 200: Array<{ + duration: number; + paths: Array; + short_id: string; + size: number; + time: number; + }>; }; export type ListSnapshotsResponse = ListSnapshotsResponses[keyof ListSnapshotsResponses]; export type GetSnapshotDetailsData = { - body?: never; - path: { - name: string; - snapshotId: string; - }; - query?: never; - url: "/api/v1/repositories/{name}/snapshots/{snapshotId}"; + body?: never; + path: { + name: string; + snapshotId: string; + }; + query?: never; + url: '/api/v1/repositories/{name}/snapshots/{snapshotId}'; }; export type GetSnapshotDetailsResponses = { - /** - * Snapshot details - */ - 200: { - duration: number; - paths: Array; - short_id: string; - size: number; - time: number; - }; + /** + * Snapshot details + */ + 200: { + duration: number; + paths: Array; + short_id: string; + size: number; + time: number; + }; }; export type GetSnapshotDetailsResponse = GetSnapshotDetailsResponses[keyof GetSnapshotDetailsResponses]; export type ListSnapshotFilesData = { - body?: never; - path: { - name: string; - snapshotId: string; - }; - query?: { - path?: string; - }; - url: "/api/v1/repositories/{name}/snapshots/{snapshotId}/files"; + body?: never; + path: { + name: string; + snapshotId: string; + }; + query?: { + path?: string; + }; + url: '/api/v1/repositories/{name}/snapshots/{snapshotId}/files'; }; export type ListSnapshotFilesResponses = { - /** - * List of files and directories in the snapshot - */ - 200: { - files: Array<{ - name: string; - path: string; - type: string; - atime?: string; - ctime?: string; - gid?: number; - mode?: number; - mtime?: string; - size?: number; - uid?: number; - }>; - snapshot: { - hostname: string; - id: string; - paths: Array; - short_id: string; - time: string; - }; - }; + /** + * List of files and directories in the snapshot + */ + 200: { + files: Array<{ + name: string; + path: string; + type: string; + atime?: string; + ctime?: string; + gid?: number; + mode?: number; + mtime?: string; + size?: number; + uid?: number; + }>; + snapshot: { + hostname: string; + id: string; + paths: Array; + short_id: string; + time: string; + }; + }; }; export type ListSnapshotFilesResponse = ListSnapshotFilesResponses[keyof ListSnapshotFilesResponses]; export type RestoreSnapshotData = { - body?: { - snapshotId: string; - delete?: boolean; - exclude?: Array; - include?: Array; - }; - path: { - name: string; - }; - query?: never; - url: "/api/v1/repositories/{name}/restore"; + body?: { + snapshotId: string; + delete?: boolean; + exclude?: Array; + include?: Array; + }; + path: { + name: string; + }; + query?: never; + url: '/api/v1/repositories/{name}/restore'; }; export type RestoreSnapshotResponses = { - /** - * Snapshot restored successfully - */ - 200: { - filesRestored: number; - filesSkipped: number; - message: string; - success: boolean; - }; + /** + * Snapshot restored successfully + */ + 200: { + filesRestored: number; + filesSkipped: number; + message: string; + success: boolean; + }; }; export type RestoreSnapshotResponse = RestoreSnapshotResponses[keyof RestoreSnapshotResponses]; export type DoctorRepositoryData = { - body?: never; - path: { - name: string; - }; - query?: never; - url: "/api/v1/repositories/{name}/doctor"; + body?: never; + path: { + name: string; + }; + query?: never; + url: '/api/v1/repositories/{name}/doctor'; }; export type DoctorRepositoryResponses = { - /** - * Doctor operation completed - */ - 200: { - steps: Array<{ - error: string | null; - output: string | null; - step: string; - success: boolean; - }>; - success: boolean; - }; + /** + * Doctor operation completed + */ + 200: { + steps: Array<{ + error: string | null; + output: string | null; + step: string; + success: boolean; + }>; + success: boolean; + }; }; export type DoctorRepositoryResponse = DoctorRepositoryResponses[keyof DoctorRepositoryResponses]; export type ListBackupSchedulesData = { - body?: never; - path?: never; - query?: never; - url: "/api/v1/backups"; + body?: never; + path?: never; + query?: never; + url: '/api/v1/backups'; }; export type ListBackupSchedulesResponses = { - /** - * List of backup schedules - */ - 200: Array<{ - createdAt: number; - cronExpression: string; - enabled: boolean; - excludePatterns: Array | null; - id: number; - includePatterns: Array | null; - lastBackupAt: number | null; - lastBackupError: string | null; - lastBackupStatus: "error" | "in_progress" | "success" | null; - nextBackupAt: number | null; - repository: { - compressionMode: "auto" | "better" | "fastest" | "max" | "off" | null; - config: - | { - accessKeyId: string; - backend: "s3"; - bucket: string; - endpoint: string; - secretAccessKey: string; - } - | { - accountKey: string; - accountName: string; - backend: "azure"; - container: string; - endpointSuffix?: string; - } - | { - backend: "gcs"; - bucket: string; - credentialsJson: string; - projectId: string; - } - | { - backend: "local"; - name: string; - } - | { - backend: "rclone"; - path: string; - remote: string; - }; - createdAt: number; - id: string; - lastChecked: number | null; - lastError: string | null; - name: string; - status: "error" | "healthy" | "unknown" | null; - type: "azure" | "gcs" | "local" | "rclone" | "s3"; - updatedAt: number; - }; - repositoryId: string; - retentionPolicy: { - keepDaily?: number; - keepHourly?: number; - keepLast?: number; - keepMonthly?: number; - keepWeekly?: number; - keepWithinDuration?: string; - keepYearly?: number; - } | null; - updatedAt: number; - volume: { - autoRemount: boolean; - config: - | { - backend: "directory"; - path: string; - readOnly?: false; - } - | { - backend: "nfs"; - exportPath: string; - server: string; - version: "3" | "4" | "4.1"; - port?: number; - readOnly?: boolean; - } - | { - backend: "smb"; - password: string; - server: string; - share: string; - username: string; - vers?: "1.0" | "2.0" | "2.1" | "3.0"; - port?: number; - domain?: string; - readOnly?: boolean; - } - | { - backend: "webdav"; - path: string; - server: string; - port?: number; - password?: string; - readOnly?: boolean; - ssl?: boolean; - username?: string; - }; - createdAt: number; - id: number; - lastError: string | null; - lastHealthCheck: number; - name: string; - status: "error" | "mounted" | "unmounted"; - type: "directory" | "nfs" | "smb" | "webdav"; - updatedAt: number; - }; - volumeId: number; - }>; + /** + * List of backup schedules + */ + 200: Array<{ + createdAt: number; + cronExpression: string; + enabled: boolean; + excludePatterns: Array | null; + id: number; + includePatterns: Array | null; + lastBackupAt: number | null; + lastBackupError: string | null; + lastBackupStatus: 'error' | 'in_progress' | 'success' | null; + nextBackupAt: number | null; + repository: { + compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null; + config: { + accessKeyId: string; + backend: 'r2'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accessKeyId: string; + backend: 's3'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accountKey: string; + accountName: string; + backend: 'azure'; + container: string; + endpointSuffix?: string; + } | { + backend: 'gcs'; + bucket: string; + credentialsJson: string; + projectId: string; + } | { + backend: 'local'; + name: string; + } | { + backend: 'rclone'; + path: string; + remote: string; + }; + createdAt: number; + id: string; + lastChecked: number | null; + lastError: string | null; + name: string; + status: 'error' | 'healthy' | 'unknown' | null; + type: 'azure' | 'gcs' | 'local' | 'r2' | 'rclone' | 's3'; + updatedAt: number; + }; + repositoryId: string; + retentionPolicy: { + keepDaily?: number; + keepHourly?: number; + keepLast?: number; + keepMonthly?: number; + keepWeekly?: number; + keepWithinDuration?: string; + keepYearly?: number; + } | null; + updatedAt: number; + volume: { + autoRemount: boolean; + config: { + backend: 'directory'; + path: string; + readOnly?: false; + } | { + backend: 'nfs'; + exportPath: string; + server: string; + version: '3' | '4' | '4.1'; + port?: number; + readOnly?: boolean; + } | { + backend: 'smb'; + password: string; + server: string; + share: string; + username: string; + vers?: '1.0' | '2.0' | '2.1' | '3.0'; + port?: number; + domain?: string; + readOnly?: boolean; + } | { + backend: 'webdav'; + path: string; + server: string; + port?: number; + password?: string; + readOnly?: boolean; + ssl?: boolean; + username?: string; + }; + createdAt: number; + id: number; + lastError: string | null; + lastHealthCheck: number; + name: string; + status: 'error' | 'mounted' | 'unmounted'; + type: 'directory' | 'nfs' | 'smb' | 'webdav'; + updatedAt: number; + }; + volumeId: number; + }>; }; export type ListBackupSchedulesResponse = ListBackupSchedulesResponses[keyof ListBackupSchedulesResponses]; export type CreateBackupScheduleData = { - body?: { - cronExpression: string; - enabled: boolean; - repositoryId: string; - volumeId: number; - excludePatterns?: Array; - includePatterns?: Array; - retentionPolicy?: { - keepDaily?: number; - keepHourly?: number; - keepLast?: number; - keepMonthly?: number; - keepWeekly?: number; - keepWithinDuration?: string; - keepYearly?: number; - }; - tags?: Array; - }; - path?: never; - query?: never; - url: "/api/v1/backups"; + body?: { + cronExpression: string; + enabled: boolean; + repositoryId: string; + volumeId: number; + excludePatterns?: Array; + includePatterns?: Array; + retentionPolicy?: { + keepDaily?: number; + keepHourly?: number; + keepLast?: number; + keepMonthly?: number; + keepWeekly?: number; + keepWithinDuration?: string; + keepYearly?: number; + }; + tags?: Array; + }; + path?: never; + query?: never; + url: '/api/v1/backups'; }; export type CreateBackupScheduleResponses = { - /** - * Backup schedule created successfully - */ - 201: { - createdAt: number; - cronExpression: string; - enabled: boolean; - excludePatterns: Array | null; - id: number; - includePatterns: Array | null; - lastBackupAt: number | null; - lastBackupError: string | null; - lastBackupStatus: "error" | "in_progress" | "success" | null; - nextBackupAt: number | null; - repositoryId: string; - retentionPolicy: { - keepDaily?: number; - keepHourly?: number; - keepLast?: number; - keepMonthly?: number; - keepWeekly?: number; - keepWithinDuration?: string; - keepYearly?: number; - } | null; - updatedAt: number; - volumeId: number; - }; + /** + * Backup schedule created successfully + */ + 201: { + createdAt: number; + cronExpression: string; + enabled: boolean; + excludePatterns: Array | null; + id: number; + includePatterns: Array | null; + lastBackupAt: number | null; + lastBackupError: string | null; + lastBackupStatus: 'error' | 'in_progress' | 'success' | null; + nextBackupAt: number | null; + repositoryId: string; + retentionPolicy: { + keepDaily?: number; + keepHourly?: number; + keepLast?: number; + keepMonthly?: number; + keepWeekly?: number; + keepWithinDuration?: string; + keepYearly?: number; + } | null; + updatedAt: number; + volumeId: number; + }; }; export type CreateBackupScheduleResponse = CreateBackupScheduleResponses[keyof CreateBackupScheduleResponses]; export type DeleteBackupScheduleData = { - body?: never; - path: { - scheduleId: string; - }; - query?: never; - url: "/api/v1/backups/{scheduleId}"; + body?: never; + path: { + scheduleId: string; + }; + query?: never; + url: '/api/v1/backups/{scheduleId}'; }; export type DeleteBackupScheduleResponses = { - /** - * Backup schedule deleted successfully - */ - 200: { - success: boolean; - }; + /** + * Backup schedule deleted successfully + */ + 200: { + success: boolean; + }; }; export type DeleteBackupScheduleResponse = DeleteBackupScheduleResponses[keyof DeleteBackupScheduleResponses]; export type GetBackupScheduleData = { - body?: never; - path: { - scheduleId: string; - }; - query?: never; - url: "/api/v1/backups/{scheduleId}"; + body?: never; + path: { + scheduleId: string; + }; + query?: never; + url: '/api/v1/backups/{scheduleId}'; }; export type GetBackupScheduleResponses = { - /** - * Backup schedule details - */ - 200: { - createdAt: number; - cronExpression: string; - enabled: boolean; - excludePatterns: Array | null; - id: number; - includePatterns: Array | null; - lastBackupAt: number | null; - lastBackupError: string | null; - lastBackupStatus: "error" | "in_progress" | "success" | null; - nextBackupAt: number | null; - repository: { - compressionMode: "auto" | "better" | "fastest" | "max" | "off" | null; - config: - | { - accessKeyId: string; - backend: "s3"; - bucket: string; - endpoint: string; - secretAccessKey: string; - } - | { - accountKey: string; - accountName: string; - backend: "azure"; - container: string; - endpointSuffix?: string; - } - | { - backend: "gcs"; - bucket: string; - credentialsJson: string; - projectId: string; - } - | { - backend: "local"; - name: string; - } - | { - backend: "rclone"; - path: string; - remote: string; - }; - createdAt: number; - id: string; - lastChecked: number | null; - lastError: string | null; - name: string; - status: "error" | "healthy" | "unknown" | null; - type: "azure" | "gcs" | "local" | "rclone" | "s3"; - updatedAt: number; - }; - repositoryId: string; - retentionPolicy: { - keepDaily?: number; - keepHourly?: number; - keepLast?: number; - keepMonthly?: number; - keepWeekly?: number; - keepWithinDuration?: string; - keepYearly?: number; - } | null; - updatedAt: number; - volume: { - autoRemount: boolean; - config: - | { - backend: "directory"; - path: string; - readOnly?: false; - } - | { - backend: "nfs"; - exportPath: string; - server: string; - version: "3" | "4" | "4.1"; - port?: number; - readOnly?: boolean; - } - | { - backend: "smb"; - password: string; - server: string; - share: string; - username: string; - vers?: "1.0" | "2.0" | "2.1" | "3.0"; - port?: number; - domain?: string; - readOnly?: boolean; - } - | { - backend: "webdav"; - path: string; - server: string; - port?: number; - password?: string; - readOnly?: boolean; - ssl?: boolean; - username?: string; - }; - createdAt: number; - id: number; - lastError: string | null; - lastHealthCheck: number; - name: string; - status: "error" | "mounted" | "unmounted"; - type: "directory" | "nfs" | "smb" | "webdav"; - updatedAt: number; - }; - volumeId: number; - }; + /** + * Backup schedule details + */ + 200: { + createdAt: number; + cronExpression: string; + enabled: boolean; + excludePatterns: Array | null; + id: number; + includePatterns: Array | null; + lastBackupAt: number | null; + lastBackupError: string | null; + lastBackupStatus: 'error' | 'in_progress' | 'success' | null; + nextBackupAt: number | null; + repository: { + compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null; + config: { + accessKeyId: string; + backend: 'r2'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accessKeyId: string; + backend: 's3'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accountKey: string; + accountName: string; + backend: 'azure'; + container: string; + endpointSuffix?: string; + } | { + backend: 'gcs'; + bucket: string; + credentialsJson: string; + projectId: string; + } | { + backend: 'local'; + name: string; + } | { + backend: 'rclone'; + path: string; + remote: string; + }; + createdAt: number; + id: string; + lastChecked: number | null; + lastError: string | null; + name: string; + status: 'error' | 'healthy' | 'unknown' | null; + type: 'azure' | 'gcs' | 'local' | 'r2' | 'rclone' | 's3'; + updatedAt: number; + }; + repositoryId: string; + retentionPolicy: { + keepDaily?: number; + keepHourly?: number; + keepLast?: number; + keepMonthly?: number; + keepWeekly?: number; + keepWithinDuration?: string; + keepYearly?: number; + } | null; + updatedAt: number; + volume: { + autoRemount: boolean; + config: { + backend: 'directory'; + path: string; + readOnly?: false; + } | { + backend: 'nfs'; + exportPath: string; + server: string; + version: '3' | '4' | '4.1'; + port?: number; + readOnly?: boolean; + } | { + backend: 'smb'; + password: string; + server: string; + share: string; + username: string; + vers?: '1.0' | '2.0' | '2.1' | '3.0'; + port?: number; + domain?: string; + readOnly?: boolean; + } | { + backend: 'webdav'; + path: string; + server: string; + port?: number; + password?: string; + readOnly?: boolean; + ssl?: boolean; + username?: string; + }; + createdAt: number; + id: number; + lastError: string | null; + lastHealthCheck: number; + name: string; + status: 'error' | 'mounted' | 'unmounted'; + type: 'directory' | 'nfs' | 'smb' | 'webdav'; + updatedAt: number; + }; + volumeId: number; + }; }; export type GetBackupScheduleResponse = GetBackupScheduleResponses[keyof GetBackupScheduleResponses]; export type UpdateBackupScheduleData = { - body?: { - cronExpression: string; - repositoryId: string; - enabled?: boolean; - excludePatterns?: Array; - includePatterns?: Array; - retentionPolicy?: { - keepDaily?: number; - keepHourly?: number; - keepLast?: number; - keepMonthly?: number; - keepWeekly?: number; - keepWithinDuration?: string; - keepYearly?: number; - }; - tags?: Array; - }; - path: { - scheduleId: string; - }; - query?: never; - url: "/api/v1/backups/{scheduleId}"; + body?: { + cronExpression: string; + repositoryId: string; + enabled?: boolean; + excludePatterns?: Array; + includePatterns?: Array; + retentionPolicy?: { + keepDaily?: number; + keepHourly?: number; + keepLast?: number; + keepMonthly?: number; + keepWeekly?: number; + keepWithinDuration?: string; + keepYearly?: number; + }; + tags?: Array; + }; + path: { + scheduleId: string; + }; + query?: never; + url: '/api/v1/backups/{scheduleId}'; }; export type UpdateBackupScheduleResponses = { - /** - * Backup schedule updated successfully - */ - 200: { - createdAt: number; - cronExpression: string; - enabled: boolean; - excludePatterns: Array | null; - id: number; - includePatterns: Array | null; - lastBackupAt: number | null; - lastBackupError: string | null; - lastBackupStatus: "error" | "in_progress" | "success" | null; - nextBackupAt: number | null; - repositoryId: string; - retentionPolicy: { - keepDaily?: number; - keepHourly?: number; - keepLast?: number; - keepMonthly?: number; - keepWeekly?: number; - keepWithinDuration?: string; - keepYearly?: number; - } | null; - updatedAt: number; - volumeId: number; - }; + /** + * Backup schedule updated successfully + */ + 200: { + createdAt: number; + cronExpression: string; + enabled: boolean; + excludePatterns: Array | null; + id: number; + includePatterns: Array | null; + lastBackupAt: number | null; + lastBackupError: string | null; + lastBackupStatus: 'error' | 'in_progress' | 'success' | null; + nextBackupAt: number | null; + repositoryId: string; + retentionPolicy: { + keepDaily?: number; + keepHourly?: number; + keepLast?: number; + keepMonthly?: number; + keepWeekly?: number; + keepWithinDuration?: string; + keepYearly?: number; + } | null; + updatedAt: number; + volumeId: number; + }; }; export type UpdateBackupScheduleResponse = UpdateBackupScheduleResponses[keyof UpdateBackupScheduleResponses]; export type GetBackupScheduleForVolumeData = { - body?: never; - path: { - volumeId: string; - }; - query?: never; - url: "/api/v1/backups/volume/{volumeId}"; + body?: never; + path: { + volumeId: string; + }; + query?: never; + url: '/api/v1/backups/volume/{volumeId}'; }; export type GetBackupScheduleForVolumeResponses = { - /** - * Backup schedule details for the volume - */ - 200: { - createdAt: number; - cronExpression: string; - enabled: boolean; - excludePatterns: Array | null; - id: number; - includePatterns: Array | null; - lastBackupAt: number | null; - lastBackupError: string | null; - lastBackupStatus: "error" | "in_progress" | "success" | null; - nextBackupAt: number | null; - repository: { - compressionMode: "auto" | "better" | "fastest" | "max" | "off" | null; - config: - | { - accessKeyId: string; - backend: "s3"; - bucket: string; - endpoint: string; - secretAccessKey: string; - } - | { - accountKey: string; - accountName: string; - backend: "azure"; - container: string; - endpointSuffix?: string; - } - | { - backend: "gcs"; - bucket: string; - credentialsJson: string; - projectId: string; - } - | { - backend: "local"; - name: string; - } - | { - backend: "rclone"; - path: string; - remote: string; - }; - createdAt: number; - id: string; - lastChecked: number | null; - lastError: string | null; - name: string; - status: "error" | "healthy" | "unknown" | null; - type: "azure" | "gcs" | "local" | "rclone" | "s3"; - updatedAt: number; - }; - repositoryId: string; - retentionPolicy: { - keepDaily?: number; - keepHourly?: number; - keepLast?: number; - keepMonthly?: number; - keepWeekly?: number; - keepWithinDuration?: string; - keepYearly?: number; - } | null; - updatedAt: number; - volume: { - autoRemount: boolean; - config: - | { - backend: "directory"; - path: string; - readOnly?: false; - } - | { - backend: "nfs"; - exportPath: string; - server: string; - version: "3" | "4" | "4.1"; - port?: number; - readOnly?: boolean; - } - | { - backend: "smb"; - password: string; - server: string; - share: string; - username: string; - vers?: "1.0" | "2.0" | "2.1" | "3.0"; - port?: number; - domain?: string; - readOnly?: boolean; - } - | { - backend: "webdav"; - path: string; - server: string; - port?: number; - password?: string; - readOnly?: boolean; - ssl?: boolean; - username?: string; - }; - createdAt: number; - id: number; - lastError: string | null; - lastHealthCheck: number; - name: string; - status: "error" | "mounted" | "unmounted"; - type: "directory" | "nfs" | "smb" | "webdav"; - updatedAt: number; - }; - volumeId: number; - } | null; + /** + * Backup schedule details for the volume + */ + 200: { + createdAt: number; + cronExpression: string; + enabled: boolean; + excludePatterns: Array | null; + id: number; + includePatterns: Array | null; + lastBackupAt: number | null; + lastBackupError: string | null; + lastBackupStatus: 'error' | 'in_progress' | 'success' | null; + nextBackupAt: number | null; + repository: { + compressionMode: 'auto' | 'better' | 'fastest' | 'max' | 'off' | null; + config: { + accessKeyId: string; + backend: 'r2'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accessKeyId: string; + backend: 's3'; + bucket: string; + endpoint: string; + secretAccessKey: string; + } | { + accountKey: string; + accountName: string; + backend: 'azure'; + container: string; + endpointSuffix?: string; + } | { + backend: 'gcs'; + bucket: string; + credentialsJson: string; + projectId: string; + } | { + backend: 'local'; + name: string; + } | { + backend: 'rclone'; + path: string; + remote: string; + }; + createdAt: number; + id: string; + lastChecked: number | null; + lastError: string | null; + name: string; + status: 'error' | 'healthy' | 'unknown' | null; + type: 'azure' | 'gcs' | 'local' | 'r2' | 'rclone' | 's3'; + updatedAt: number; + }; + repositoryId: string; + retentionPolicy: { + keepDaily?: number; + keepHourly?: number; + keepLast?: number; + keepMonthly?: number; + keepWeekly?: number; + keepWithinDuration?: string; + keepYearly?: number; + } | null; + updatedAt: number; + volume: { + autoRemount: boolean; + config: { + backend: 'directory'; + path: string; + readOnly?: false; + } | { + backend: 'nfs'; + exportPath: string; + server: string; + version: '3' | '4' | '4.1'; + port?: number; + readOnly?: boolean; + } | { + backend: 'smb'; + password: string; + server: string; + share: string; + username: string; + vers?: '1.0' | '2.0' | '2.1' | '3.0'; + port?: number; + domain?: string; + readOnly?: boolean; + } | { + backend: 'webdav'; + path: string; + server: string; + port?: number; + password?: string; + readOnly?: boolean; + ssl?: boolean; + username?: string; + }; + createdAt: number; + id: number; + lastError: string | null; + lastHealthCheck: number; + name: string; + status: 'error' | 'mounted' | 'unmounted'; + type: 'directory' | 'nfs' | 'smb' | 'webdav'; + updatedAt: number; + }; + volumeId: number; + } | null; }; -export type GetBackupScheduleForVolumeResponse = - GetBackupScheduleForVolumeResponses[keyof GetBackupScheduleForVolumeResponses]; +export type GetBackupScheduleForVolumeResponse = GetBackupScheduleForVolumeResponses[keyof GetBackupScheduleForVolumeResponses]; export type RunBackupNowData = { - body?: never; - path: { - scheduleId: string; - }; - query?: never; - url: "/api/v1/backups/{scheduleId}/run"; + body?: never; + path: { + scheduleId: string; + }; + query?: never; + url: '/api/v1/backups/{scheduleId}/run'; }; export type RunBackupNowResponses = { - /** - * Backup started successfully - */ - 200: { - success: boolean; - }; + /** + * Backup started successfully + */ + 200: { + success: boolean; + }; }; export type RunBackupNowResponse = RunBackupNowResponses[keyof RunBackupNowResponses]; export type StopBackupData = { - body?: never; - path: { - scheduleId: string; - }; - query?: never; - url: "/api/v1/backups/{scheduleId}/stop"; + body?: never; + path: { + scheduleId: string; + }; + query?: never; + url: '/api/v1/backups/{scheduleId}/stop'; }; export type StopBackupErrors = { - /** - * No backup is currently running for this schedule - */ - 409: unknown; + /** + * No backup is currently running for this schedule + */ + 409: unknown; }; export type StopBackupResponses = { - /** - * Backup stopped successfully - */ - 200: { - success: boolean; - }; + /** + * Backup stopped successfully + */ + 200: { + success: boolean; + }; }; export type StopBackupResponse = StopBackupResponses[keyof StopBackupResponses]; +export type RunForgetData = { + body?: never; + path: { + scheduleId: string; + }; + query?: never; + url: '/api/v1/backups/{scheduleId}/forget'; +}; + +export type RunForgetErrors = { + /** + * No retention policy configured for this schedule + */ + 400: unknown; +}; + +export type RunForgetResponses = { + /** + * Retention policy applied successfully + */ + 200: { + success: boolean; + }; +}; + +export type RunForgetResponse = RunForgetResponses[keyof RunForgetResponses]; + export type GetSystemInfoData = { - body?: never; - path?: never; - query?: never; - url: "/api/v1/system/info"; + body?: never; + path?: never; + query?: never; + url: '/api/v1/system/info'; }; export type GetSystemInfoResponses = { - /** - * System information with enabled capabilities - */ - 200: { - capabilities: { - docker: boolean; - rclone: boolean; - }; - }; + /** + * System information with enabled capabilities + */ + 200: { + capabilities: { + docker: boolean; + rclone: boolean; + }; + }; }; export type GetSystemInfoResponse = GetSystemInfoResponses[keyof GetSystemInfoResponses]; export type DownloadResticPasswordData = { - body?: { - password: string; - }; - path?: never; - query?: never; - url: "/api/v1/system/restic-password"; + body?: { + password: string; + }; + path?: never; + query?: never; + url: '/api/v1/system/restic-password'; }; export type DownloadResticPasswordResponses = { - /** - * Restic password file content - */ - 200: string; + /** + * Restic password file content + */ + 200: string; }; export type DownloadResticPasswordResponse = DownloadResticPasswordResponses[keyof DownloadResticPasswordResponses]; diff --git a/app/client/modules/backups/components/schedule-summary.tsx b/app/client/modules/backups/components/schedule-summary.tsx index 49aa114..ba18d80 100644 --- a/app/client/modules/backups/components/schedule-summary.tsx +++ b/app/client/modules/backups/components/schedule-summary.tsx @@ -1,4 +1,4 @@ -import { Pencil, Play, Square, Trash2 } from "lucide-react"; +import { Eraser, Pencil, Play, Square, Trash2 } from "lucide-react"; import { useMemo, useState } from "react"; import { OnOff } from "~/client/components/onoff"; import { Button } from "~/client/components/ui/button"; @@ -14,6 +14,10 @@ import { } from "~/client/components/ui/alert-dialog"; import type { BackupSchedule } from "~/client/lib/types"; import { BackupProgressCard } from "./backup-progress-card"; +import { runForgetMutation } from "~/client/api-client/@tanstack/react-query.gen"; +import { useMutation } from "@tanstack/react-query"; +import { toast } from "sonner"; +import { parseError } from "~/client/lib/errors"; type Props = { schedule: BackupSchedule; @@ -28,6 +32,17 @@ export const ScheduleSummary = (props: Props) => { const { schedule, handleToggleEnabled, handleRunBackupNow, handleStopBackup, handleDeleteSchedule, setIsEditMode } = props; const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); + const [showForgetConfirm, setShowForgetConfirm] = useState(false); + + const runForget = useMutation({ + ...runForgetMutation(), + onSuccess: () => { + toast.success("Retention policy applied successfully"); + }, + onError: (error) => { + toast.error("Failed to apply retention policy", { description: parseError(error)?.message }); + }, + }); const summary = useMemo(() => { const scheduleLabel = schedule ? schedule.cronExpression : "-"; @@ -56,6 +71,11 @@ export const ScheduleSummary = (props: Props) => { handleDeleteSchedule(); }; + const handleConfirmForget = () => { + setShowForgetConfirm(false); + runForget.mutate({ path: { scheduleId: schedule.id.toString() } }); + }; + return (
@@ -89,6 +109,18 @@ export const ScheduleSummary = (props: Props) => { Backup now )} + {schedule.retentionPolicy && ( + + )}
+ + + + + Run retention policy cleanup? + + This will apply the retention policy and permanently delete old snapshots according to the configured + rules ({summary.retentionLabel}). This action cannot be undone. + + +
+ Cancel + Run cleanup +
+
+
); }; diff --git a/app/server/modules/backups/backups.controller.ts b/app/server/modules/backups/backups.controller.ts index 67c63c5..e941af0 100644 --- a/app/server/modules/backups/backups.controller.ts +++ b/app/server/modules/backups/backups.controller.ts @@ -8,6 +8,7 @@ import { getBackupScheduleForVolumeDto, listBackupSchedulesDto, runBackupNowDto, + runForgetDto, stopBackupDto, updateBackupScheduleDto, updateBackupScheduleBody, @@ -17,6 +18,7 @@ import { type GetBackupScheduleForVolumeResponseDto, type ListBackupSchedulesResponseDto, type RunBackupNowDto, + type RunForgetDto, type StopBackupDto, type UpdateBackupScheduleDto, } from "./backups.dto"; @@ -78,4 +80,11 @@ export const backupScheduleController = new Hono() await backupsService.stopBackup(Number(scheduleId)); return c.json({ success: true }, 200); + }) + .post("/:scheduleId/forget", runForgetDto, async (c) => { + const scheduleId = c.req.param("scheduleId"); + + await backupsService.runForget(Number(scheduleId)); + + return c.json({ success: true }, 200); }); diff --git a/app/server/modules/backups/backups.dto.ts b/app/server/modules/backups/backups.dto.ts index 207501f..8e11e77 100644 --- a/app/server/modules/backups/backups.dto.ts +++ b/app/server/modules/backups/backups.dto.ts @@ -251,3 +251,28 @@ export const stopBackupDto = describeRoute({ }, }, }); + +/** + * Run retention policy (forget) manually + */ +export const runForgetResponse = type({ + success: "boolean", +}); + +export type RunForgetDto = typeof runForgetResponse.infer; + +export const runForgetDto = describeRoute({ + description: "Manually apply retention policy to clean up old snapshots", + operationId: "runForget", + tags: ["Backups"], + responses: { + 200: { + description: "Retention policy applied successfully", + content: { + "application/json": { + schema: resolver(runForgetResponse), + }, + }, + }, + }, +}); diff --git a/app/server/modules/backups/backups.service.ts b/app/server/modules/backups/backups.service.ts index da119aa..2632277 100644 --- a/app/server/modules/backups/backups.service.ts +++ b/app/server/modules/backups/backups.service.ts @@ -342,6 +342,32 @@ const stopBackup = async (scheduleId: number) => { abortController.abort(); }; +const runForget = async (scheduleId: number) => { + const schedule = await db.query.backupSchedulesTable.findFirst({ + where: eq(backupSchedulesTable.id, scheduleId), + }); + + if (!schedule) { + throw new NotFoundError("Backup schedule not found"); + } + + if (!schedule.retentionPolicy) { + throw new BadRequestError("No retention policy configured for this schedule"); + } + + const repository = await db.query.repositoriesTable.findFirst({ + where: eq(repositoriesTable.id, schedule.repositoryId), + }); + + if (!repository) { + throw new NotFoundError("Repository not found"); + } + + logger.info(`Manually running retention policy (forget) for schedule ${scheduleId}`); + await restic.forget(repository.config, schedule.retentionPolicy, { tag: schedule.id.toString() }); + logger.info(`Retention policy applied successfully for schedule ${scheduleId}`); +}; + export const backupsService = { listSchedules, getSchedule, @@ -352,4 +378,5 @@ export const backupsService = { getSchedulesToExecute, getScheduleForVolume, stopBackup, + runForget, };