feat: custom flags allowlist
This commit is contained in:
parent
0c20473e30
commit
157be2d6ab
14 changed files with 3868 additions and 3471 deletions
|
|
@ -4,8 +4,8 @@
|
|||
import { type DefaultError, type InfiniteData, infiniteQueryOptions, queryOptions, type UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
import { client } from '../client.gen';
|
||||
import { browseFilesystem, cancelDoctor, createBackupSchedule, createNotificationDestination, createRepository, createVolume, deleteBackupSchedule, deleteNotificationDestination, deleteRepository, deleteSnapshot, deleteSnapshots, deleteSsoInvitation, deleteSsoProvider, deleteUserAccount, deleteVolume, devPanelExec, downloadResticPassword, dumpSnapshot, getAdminUsers, getBackupProgress, getBackupSchedule, getBackupScheduleForVolume, getDevPanel, getMirrorCompatibility, getNotificationDestination, getOrgMembers, getPublicSsoProviders, getRegistrationStatus, getRepository, getRepositoryStats, getScheduleMirrors, getScheduleNotifications, getSnapshotDetails, getSsoSettings, getStatus, getSystemInfo, getUpdates, getUserDeletionImpact, getVolume, healthCheckVolume, listBackupSchedules, listFiles, listNotificationDestinations, listRcloneRemotes, listRepositories, listSnapshotFiles, listSnapshots, listVolumes, mountVolume, type Options, refreshSnapshots, removeOrgMember, reorderBackupSchedules, restoreSnapshot, runBackupNow, runForget, setRegistrationStatus, startDoctor, stopBackup, tagSnapshots, testConnection, testNotificationDestination, unlockRepository, unmountVolume, updateBackupSchedule, updateMemberRole, updateNotificationDestination, updateRepository, updateScheduleMirrors, updateScheduleNotifications, updateSsoProviderAutoLinking, updateVolume } from '../sdk.gen';
|
||||
import type { BrowseFilesystemData, BrowseFilesystemResponse, CancelDoctorData, CancelDoctorResponse, CreateBackupScheduleData, CreateBackupScheduleResponse, CreateNotificationDestinationData, CreateNotificationDestinationResponse, CreateRepositoryData, CreateRepositoryResponse, CreateVolumeData, CreateVolumeResponse, DeleteBackupScheduleData, DeleteBackupScheduleResponse, DeleteNotificationDestinationData, DeleteNotificationDestinationResponse, DeleteRepositoryData, DeleteRepositoryResponse, DeleteSnapshotData, DeleteSnapshotResponse, DeleteSnapshotsData, DeleteSnapshotsResponse, DeleteSsoInvitationData, DeleteSsoProviderData, DeleteUserAccountData, DeleteVolumeData, DeleteVolumeResponse, DevPanelExecData, DevPanelExecResponse, DownloadResticPasswordData, DownloadResticPasswordResponse, DumpSnapshotData, DumpSnapshotResponse, GetAdminUsersData, GetAdminUsersResponse, GetBackupProgressData, GetBackupProgressResponse, GetBackupScheduleData, GetBackupScheduleForVolumeData, GetBackupScheduleForVolumeResponse, GetBackupScheduleResponse, GetDevPanelData, GetDevPanelResponse, GetMirrorCompatibilityData, GetMirrorCompatibilityResponse, GetNotificationDestinationData, GetNotificationDestinationResponse, GetOrgMembersData, GetOrgMembersResponse, GetPublicSsoProvidersData, GetPublicSsoProvidersResponse, GetRegistrationStatusData, GetRegistrationStatusResponse, GetRepositoryData, GetRepositoryResponse, GetRepositoryStatsData, GetRepositoryStatsResponse, GetScheduleMirrorsData, GetScheduleMirrorsResponse, GetScheduleNotificationsData, GetScheduleNotificationsResponse, GetSnapshotDetailsData, GetSnapshotDetailsResponse, GetSsoSettingsData, GetSsoSettingsResponse, GetStatusData, GetStatusResponse, GetSystemInfoData, GetSystemInfoResponse, GetUpdatesData, GetUpdatesResponse, GetUserDeletionImpactData, GetUserDeletionImpactResponse, GetVolumeData, GetVolumeResponse, HealthCheckVolumeData, HealthCheckVolumeResponse, ListBackupSchedulesData, ListBackupSchedulesResponse, ListFilesData, ListFilesResponse, ListNotificationDestinationsData, ListNotificationDestinationsResponse, ListRcloneRemotesData, ListRcloneRemotesResponse, ListRepositoriesData, ListRepositoriesResponse, ListSnapshotFilesData, ListSnapshotFilesResponse, ListSnapshotsData, ListSnapshotsResponse, ListVolumesData, ListVolumesResponse, MountVolumeData, MountVolumeResponse, RefreshSnapshotsData, RefreshSnapshotsResponse, RemoveOrgMemberData, ReorderBackupSchedulesData, ReorderBackupSchedulesResponse, RestoreSnapshotData, RestoreSnapshotResponse, RunBackupNowData, RunBackupNowResponse, RunForgetData, RunForgetResponse, SetRegistrationStatusData, SetRegistrationStatusResponse, StartDoctorData, StartDoctorResponse, StopBackupData, StopBackupResponse, TagSnapshotsData, TagSnapshotsResponse, TestConnectionData, TestConnectionResponse, TestNotificationDestinationData, TestNotificationDestinationResponse, UnlockRepositoryData, UnlockRepositoryResponse, UnmountVolumeData, UnmountVolumeResponse, UpdateBackupScheduleData, UpdateBackupScheduleResponse, UpdateMemberRoleData, UpdateNotificationDestinationData, UpdateNotificationDestinationResponse, UpdateRepositoryData, UpdateRepositoryResponse, UpdateScheduleMirrorsData, UpdateScheduleMirrorsResponse, UpdateScheduleNotificationsData, UpdateScheduleNotificationsResponse, UpdateSsoProviderAutoLinkingData, UpdateVolumeData, UpdateVolumeResponse } from '../types.gen';
|
||||
import { browseFilesystem, cancelDoctor, createBackupSchedule, createNotificationDestination, createRepository, createVolume, deleteBackupSchedule, deleteNotificationDestination, deleteRepository, deleteSnapshot, deleteSnapshots, deleteSsoInvitation, deleteSsoProvider, deleteUserAccount, deleteVolume, downloadResticPassword, dumpSnapshot, getAdminUsers, getBackupProgress, getBackupSchedule, getBackupScheduleForVolume, getDevPanel, getMirrorCompatibility, getNotificationDestination, getOrgMembers, getPublicSsoProviders, getRegistrationStatus, getRepository, getRepositoryStats, getScheduleMirrors, getScheduleNotifications, getSnapshotDetails, getSsoSettings, getStatus, getSystemInfo, getUpdates, getUserDeletionImpact, getVolume, healthCheckVolume, listBackupSchedules, listFiles, listNotificationDestinations, listRcloneRemotes, listRepositories, listSnapshotFiles, listSnapshots, listVolumes, mountVolume, type Options, refreshSnapshots, removeOrgMember, reorderBackupSchedules, restoreSnapshot, runBackupNow, runForget, setRegistrationStatus, startDoctor, stopBackup, tagSnapshots, testConnection, testNotificationDestination, unlockRepository, unmountVolume, updateBackupSchedule, updateMemberRole, updateNotificationDestination, updateRepository, updateScheduleMirrors, updateScheduleNotifications, updateSsoProviderAutoLinking, updateVolume } from '../sdk.gen';
|
||||
import type { BrowseFilesystemData, BrowseFilesystemResponse, CancelDoctorData, CancelDoctorResponse, CreateBackupScheduleData, CreateBackupScheduleResponse, CreateNotificationDestinationData, CreateNotificationDestinationResponse, CreateRepositoryData, CreateRepositoryResponse, CreateVolumeData, CreateVolumeResponse, DeleteBackupScheduleData, DeleteBackupScheduleResponse, DeleteNotificationDestinationData, DeleteNotificationDestinationResponse, DeleteRepositoryData, DeleteRepositoryResponse, DeleteSnapshotData, DeleteSnapshotResponse, DeleteSnapshotsData, DeleteSnapshotsResponse, DeleteSsoInvitationData, DeleteSsoProviderData, DeleteUserAccountData, DeleteVolumeData, DeleteVolumeResponse, DownloadResticPasswordData, DownloadResticPasswordResponse, DumpSnapshotData, DumpSnapshotResponse, GetAdminUsersData, GetAdminUsersResponse, GetBackupProgressData, GetBackupProgressResponse, GetBackupScheduleData, GetBackupScheduleForVolumeData, GetBackupScheduleForVolumeResponse, GetBackupScheduleResponse, GetDevPanelData, GetDevPanelResponse, GetMirrorCompatibilityData, GetMirrorCompatibilityResponse, GetNotificationDestinationData, GetNotificationDestinationResponse, GetOrgMembersData, GetOrgMembersResponse, GetPublicSsoProvidersData, GetPublicSsoProvidersResponse, GetRegistrationStatusData, GetRegistrationStatusResponse, GetRepositoryData, GetRepositoryResponse, GetRepositoryStatsData, GetRepositoryStatsResponse, GetScheduleMirrorsData, GetScheduleMirrorsResponse, GetScheduleNotificationsData, GetScheduleNotificationsResponse, GetSnapshotDetailsData, GetSnapshotDetailsResponse, GetSsoSettingsData, GetSsoSettingsResponse, GetStatusData, GetStatusResponse, GetSystemInfoData, GetSystemInfoResponse, GetUpdatesData, GetUpdatesResponse, GetUserDeletionImpactData, GetUserDeletionImpactResponse, GetVolumeData, GetVolumeResponse, HealthCheckVolumeData, HealthCheckVolumeResponse, ListBackupSchedulesData, ListBackupSchedulesResponse, ListFilesData, ListFilesResponse, ListNotificationDestinationsData, ListNotificationDestinationsResponse, ListRcloneRemotesData, ListRcloneRemotesResponse, ListRepositoriesData, ListRepositoriesResponse, ListSnapshotFilesData, ListSnapshotFilesResponse, ListSnapshotsData, ListSnapshotsResponse, ListVolumesData, ListVolumesResponse, MountVolumeData, MountVolumeResponse, RefreshSnapshotsData, RefreshSnapshotsResponse, RemoveOrgMemberData, ReorderBackupSchedulesData, ReorderBackupSchedulesResponse, RestoreSnapshotData, RestoreSnapshotResponse, RunBackupNowData, RunBackupNowResponse, RunForgetData, RunForgetResponse, SetRegistrationStatusData, SetRegistrationStatusResponse, StartDoctorData, StartDoctorResponse, StopBackupData, StopBackupResponse, TagSnapshotsData, TagSnapshotsResponse, TestConnectionData, TestConnectionResponse, TestNotificationDestinationData, TestNotificationDestinationResponse, UnlockRepositoryData, UnlockRepositoryResponse, UnmountVolumeData, UnmountVolumeResponse, UpdateBackupScheduleData, UpdateBackupScheduleResponse, UpdateMemberRoleData, UpdateNotificationDestinationData, UpdateNotificationDestinationResponse, UpdateRepositoryData, UpdateRepositoryResponse, UpdateScheduleMirrorsData, UpdateScheduleMirrorsResponse, UpdateScheduleNotificationsData, UpdateScheduleNotificationsResponse, UpdateSsoProviderAutoLinkingData, UpdateVolumeData, UpdateVolumeResponse } from '../types.gen';
|
||||
|
||||
export type QueryKey<TOptions extends Options> = [
|
||||
Pick<TOptions, 'baseUrl' | 'body' | 'headers' | 'path' | 'query'> & {
|
||||
|
|
@ -58,93 +58,6 @@ export const getStatusOptions = (options?: Options<GetStatusData>) => queryOptio
|
|||
queryKey: getStatusQueryKey(options)
|
||||
});
|
||||
|
||||
export const getPublicSsoProvidersQueryKey = (options?: Options<GetPublicSsoProvidersData>) => createQueryKey('getPublicSsoProviders', options);
|
||||
|
||||
/**
|
||||
* Get public SSO providers for the instance
|
||||
*/
|
||||
export const getPublicSsoProvidersOptions = (options?: Options<GetPublicSsoProvidersData>) => queryOptions<GetPublicSsoProvidersResponse, DefaultError, GetPublicSsoProvidersResponse, ReturnType<typeof getPublicSsoProvidersQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await getPublicSsoProviders({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: getPublicSsoProvidersQueryKey(options)
|
||||
});
|
||||
|
||||
export const getSsoSettingsQueryKey = (options?: Options<GetSsoSettingsData>) => createQueryKey('getSsoSettings', options);
|
||||
|
||||
/**
|
||||
* Get SSO providers and invitations for the active organization
|
||||
*/
|
||||
export const getSsoSettingsOptions = (options?: Options<GetSsoSettingsData>) => queryOptions<GetSsoSettingsResponse, DefaultError, GetSsoSettingsResponse, ReturnType<typeof getSsoSettingsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await getSsoSettings({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: getSsoSettingsQueryKey(options)
|
||||
});
|
||||
|
||||
/**
|
||||
* Delete an SSO provider
|
||||
*/
|
||||
export const deleteSsoProviderMutation = (options?: Partial<Options<DeleteSsoProviderData>>): UseMutationOptions<unknown, DefaultError, Options<DeleteSsoProviderData>> => {
|
||||
const mutationOptions: UseMutationOptions<unknown, DefaultError, Options<DeleteSsoProviderData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await deleteSsoProvider({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update whether SSO sign-in can auto-link existing accounts by email
|
||||
*/
|
||||
export const updateSsoProviderAutoLinkingMutation = (options?: Partial<Options<UpdateSsoProviderAutoLinkingData>>): UseMutationOptions<unknown, DefaultError, Options<UpdateSsoProviderAutoLinkingData>> => {
|
||||
const mutationOptions: UseMutationOptions<unknown, DefaultError, Options<UpdateSsoProviderAutoLinkingData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await updateSsoProviderAutoLinking({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete an SSO invitation
|
||||
*/
|
||||
export const deleteSsoInvitationMutation = (options?: Partial<Options<DeleteSsoInvitationData>>): UseMutationOptions<unknown, DefaultError, Options<DeleteSsoInvitationData>> => {
|
||||
const mutationOptions: UseMutationOptions<unknown, DefaultError, Options<DeleteSsoInvitationData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await deleteSsoInvitation({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const getAdminUsersQueryKey = (options?: Options<GetAdminUsersData>) => createQueryKey('getAdminUsers', options);
|
||||
|
||||
/**
|
||||
|
|
@ -250,6 +163,93 @@ export const removeOrgMemberMutation = (options?: Partial<Options<RemoveOrgMembe
|
|||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const getPublicSsoProvidersQueryKey = (options?: Options<GetPublicSsoProvidersData>) => createQueryKey('getPublicSsoProviders', options);
|
||||
|
||||
/**
|
||||
* Get public SSO providers for the instance
|
||||
*/
|
||||
export const getPublicSsoProvidersOptions = (options?: Options<GetPublicSsoProvidersData>) => queryOptions<GetPublicSsoProvidersResponse, DefaultError, GetPublicSsoProvidersResponse, ReturnType<typeof getPublicSsoProvidersQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await getPublicSsoProviders({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: getPublicSsoProvidersQueryKey(options)
|
||||
});
|
||||
|
||||
export const getSsoSettingsQueryKey = (options?: Options<GetSsoSettingsData>) => createQueryKey('getSsoSettings', options);
|
||||
|
||||
/**
|
||||
* Get SSO providers and invitations for the active organization
|
||||
*/
|
||||
export const getSsoSettingsOptions = (options?: Options<GetSsoSettingsData>) => queryOptions<GetSsoSettingsResponse, DefaultError, GetSsoSettingsResponse, ReturnType<typeof getSsoSettingsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await getSsoSettings({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: getSsoSettingsQueryKey(options)
|
||||
});
|
||||
|
||||
/**
|
||||
* Delete an SSO provider
|
||||
*/
|
||||
export const deleteSsoProviderMutation = (options?: Partial<Options<DeleteSsoProviderData>>): UseMutationOptions<unknown, DefaultError, Options<DeleteSsoProviderData>> => {
|
||||
const mutationOptions: UseMutationOptions<unknown, DefaultError, Options<DeleteSsoProviderData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await deleteSsoProvider({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update whether SSO sign-in can auto-link existing accounts by email
|
||||
*/
|
||||
export const updateSsoProviderAutoLinkingMutation = (options?: Partial<Options<UpdateSsoProviderAutoLinkingData>>): UseMutationOptions<unknown, DefaultError, Options<UpdateSsoProviderAutoLinkingData>> => {
|
||||
const mutationOptions: UseMutationOptions<unknown, DefaultError, Options<UpdateSsoProviderAutoLinkingData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await updateSsoProviderAutoLinking({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete an SSO invitation
|
||||
*/
|
||||
export const deleteSsoInvitationMutation = (options?: Partial<Options<DeleteSsoInvitationData>>): UseMutationOptions<unknown, DefaultError, Options<DeleteSsoInvitationData>> => {
|
||||
const mutationOptions: UseMutationOptions<unknown, DefaultError, Options<DeleteSsoInvitationData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await deleteSsoInvitation({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const listVolumesQueryKey = (options?: Options<ListVolumesData>) => createQueryKey('listVolumes', options);
|
||||
|
||||
/**
|
||||
|
|
@ -855,23 +855,6 @@ export const tagSnapshotsMutation = (options?: Partial<Options<TagSnapshotsData>
|
|||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a restic command against a repository (dev panel only)
|
||||
*/
|
||||
export const devPanelExecMutation = (options?: Partial<Options<DevPanelExecData>>): UseMutationOptions<DevPanelExecResponse, DefaultError, Options<DevPanelExecData>> => {
|
||||
const mutationOptions: UseMutationOptions<DevPanelExecResponse, DefaultError, Options<DevPanelExecData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await devPanelExec({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const listBackupSchedulesQueryKey = (options?: Options<ListBackupSchedulesData>) => createQueryKey('listBackupSchedules', options);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ export type Client = CoreClient<RequestFn, Config, MethodFn, BuildUrlFn, SseFn>
|
|||
*/
|
||||
export type CreateClientConfig<T extends ClientOptions = ClientOptions> = (
|
||||
override?: Config<ClientOptions & T>,
|
||||
) => Config<Required<ClientOptions> & T>;
|
||||
) => Config<Required<ClientOptions> & T> | Promise<Config<Required<ClientOptions> & T>>;
|
||||
|
||||
export interface TDataShape {
|
||||
body?: unknown;
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ interface Params {
|
|||
|
||||
const stripEmptySlots = (params: Params) => {
|
||||
for (const [slot, value] of Object.entries(params)) {
|
||||
if (value && typeof value === 'object' && !Object.keys(value).length) {
|
||||
if (value && typeof value === 'object' && !Array.isArray(value) && !Object.keys(value).length) {
|
||||
delete params[slot as Slot];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,38 +24,6 @@ export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends
|
|||
*/
|
||||
export const getStatus = <ThrowOnError extends boolean = false>(options?: Options<GetStatusData, ThrowOnError>) => (options?.client ?? client).get<GetStatusResponses, unknown, ThrowOnError>({ url: '/api/v1/auth/status', ...options });
|
||||
|
||||
/**
|
||||
* Get public SSO providers for the instance
|
||||
*/
|
||||
export const getPublicSsoProviders = <ThrowOnError extends boolean = false>(options?: Options<GetPublicSsoProvidersData, ThrowOnError>) => (options?.client ?? client).get<GetPublicSsoProvidersResponses, unknown, ThrowOnError>({ url: '/api/v1/auth/sso-providers', ...options });
|
||||
|
||||
/**
|
||||
* Get SSO providers and invitations for the active organization
|
||||
*/
|
||||
export const getSsoSettings = <ThrowOnError extends boolean = false>(options?: Options<GetSsoSettingsData, ThrowOnError>) => (options?.client ?? client).get<GetSsoSettingsResponses, unknown, ThrowOnError>({ url: '/api/v1/auth/sso-settings', ...options });
|
||||
|
||||
/**
|
||||
* Delete an SSO provider
|
||||
*/
|
||||
export const deleteSsoProvider = <ThrowOnError extends boolean = false>(options: Options<DeleteSsoProviderData, ThrowOnError>) => (options.client ?? client).delete<DeleteSsoProviderResponses, DeleteSsoProviderErrors, ThrowOnError>({ url: '/api/v1/auth/sso-providers/{providerId}', ...options });
|
||||
|
||||
/**
|
||||
* Update whether SSO sign-in can auto-link existing accounts by email
|
||||
*/
|
||||
export const updateSsoProviderAutoLinking = <ThrowOnError extends boolean = false>(options: Options<UpdateSsoProviderAutoLinkingData, ThrowOnError>) => (options.client ?? client).patch<UpdateSsoProviderAutoLinkingResponses, UpdateSsoProviderAutoLinkingErrors, ThrowOnError>({
|
||||
url: '/api/v1/auth/sso-providers/{providerId}/auto-linking',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Delete an SSO invitation
|
||||
*/
|
||||
export const deleteSsoInvitation = <ThrowOnError extends boolean = false>(options: Options<DeleteSsoInvitationData, ThrowOnError>) => (options.client ?? client).delete<DeleteSsoInvitationResponses, DeleteSsoInvitationErrors, ThrowOnError>({ url: '/api/v1/auth/sso-invitations/{invitationId}', ...options });
|
||||
|
||||
/**
|
||||
* List admin users for settings management
|
||||
*/
|
||||
|
|
@ -93,6 +61,38 @@ export const updateMemberRole = <ThrowOnError extends boolean = false>(options:
|
|||
*/
|
||||
export const removeOrgMember = <ThrowOnError extends boolean = false>(options: Options<RemoveOrgMemberData, ThrowOnError>) => (options.client ?? client).delete<RemoveOrgMemberResponses, RemoveOrgMemberErrors, ThrowOnError>({ url: '/api/v1/auth/org-members/{memberId}', ...options });
|
||||
|
||||
/**
|
||||
* Get public SSO providers for the instance
|
||||
*/
|
||||
export const getPublicSsoProviders = <ThrowOnError extends boolean = false>(options?: Options<GetPublicSsoProvidersData, ThrowOnError>) => (options?.client ?? client).get<GetPublicSsoProvidersResponses, unknown, ThrowOnError>({ url: '/api/v1/auth/sso-providers', ...options });
|
||||
|
||||
/**
|
||||
* Get SSO providers and invitations for the active organization
|
||||
*/
|
||||
export const getSsoSettings = <ThrowOnError extends boolean = false>(options?: Options<GetSsoSettingsData, ThrowOnError>) => (options?.client ?? client).get<GetSsoSettingsResponses, unknown, ThrowOnError>({ url: '/api/v1/auth/sso-settings', ...options });
|
||||
|
||||
/**
|
||||
* Delete an SSO provider
|
||||
*/
|
||||
export const deleteSsoProvider = <ThrowOnError extends boolean = false>(options: Options<DeleteSsoProviderData, ThrowOnError>) => (options.client ?? client).delete<DeleteSsoProviderResponses, DeleteSsoProviderErrors, ThrowOnError>({ url: '/api/v1/auth/sso-providers/{providerId}', ...options });
|
||||
|
||||
/**
|
||||
* Update whether SSO sign-in can auto-link existing accounts by email
|
||||
*/
|
||||
export const updateSsoProviderAutoLinking = <ThrowOnError extends boolean = false>(options: Options<UpdateSsoProviderAutoLinkingData, ThrowOnError>) => (options.client ?? client).patch<UpdateSsoProviderAutoLinkingResponses, UpdateSsoProviderAutoLinkingErrors, ThrowOnError>({
|
||||
url: '/api/v1/auth/sso-providers/{providerId}/auto-linking',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Delete an SSO invitation
|
||||
*/
|
||||
export const deleteSsoInvitation = <ThrowOnError extends boolean = false>(options: Options<DeleteSsoInvitationData, ThrowOnError>) => (options.client ?? client).delete<DeleteSsoInvitationResponses, DeleteSsoInvitationErrors, ThrowOnError>({ url: '/api/v1/auth/sso-invitations/{invitationId}', ...options });
|
||||
|
||||
/**
|
||||
* List all volumes
|
||||
*/
|
||||
|
|
@ -101,24 +101,24 @@ export const listVolumes = <ThrowOnError extends boolean = false>(options?: Opti
|
|||
/**
|
||||
* Create a new volume
|
||||
*/
|
||||
export const createVolume = <ThrowOnError extends boolean = false>(options?: Options<CreateVolumeData, ThrowOnError>) => (options?.client ?? client).post<CreateVolumeResponses, unknown, ThrowOnError>({
|
||||
export const createVolume = <ThrowOnError extends boolean = false>(options: Options<CreateVolumeData, ThrowOnError>) => (options.client ?? client).post<CreateVolumeResponses, unknown, ThrowOnError>({
|
||||
url: '/api/v1/volumes',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test connection to backend
|
||||
*/
|
||||
export const testConnection = <ThrowOnError extends boolean = false>(options?: Options<TestConnectionData, ThrowOnError>) => (options?.client ?? client).post<TestConnectionResponses, unknown, ThrowOnError>({
|
||||
export const testConnection = <ThrowOnError extends boolean = false>(options: Options<TestConnectionData, ThrowOnError>) => (options.client ?? client).post<TestConnectionResponses, unknown, ThrowOnError>({
|
||||
url: '/api/v1/volumes/test-connection',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -177,12 +177,12 @@ export const listRepositories = <ThrowOnError extends boolean = false>(options?:
|
|||
/**
|
||||
* Create a new restic repository
|
||||
*/
|
||||
export const createRepository = <ThrowOnError extends boolean = false>(options?: Options<CreateRepositoryData, ThrowOnError>) => (options?.client ?? client).post<CreateRepositoryResponses, unknown, ThrowOnError>({
|
||||
export const createRepository = <ThrowOnError extends boolean = false>(options: Options<CreateRepositoryData, ThrowOnError>) => (options.client ?? client).post<CreateRepositoryResponses, unknown, ThrowOnError>({
|
||||
url: '/api/v1/repositories',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -319,12 +319,12 @@ export const listBackupSchedules = <ThrowOnError extends boolean = false>(option
|
|||
/**
|
||||
* Create a new backup schedule for a volume
|
||||
*/
|
||||
export const createBackupSchedule = <ThrowOnError extends boolean = false>(options?: Options<CreateBackupScheduleData, ThrowOnError>) => (options?.client ?? client).post<CreateBackupScheduleResponses, unknown, ThrowOnError>({
|
||||
export const createBackupSchedule = <ThrowOnError extends boolean = false>(options: Options<CreateBackupScheduleData, ThrowOnError>) => (options.client ?? client).post<CreateBackupScheduleResponses, unknown, ThrowOnError>({
|
||||
url: '/api/v1/backups',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -412,12 +412,12 @@ export const getMirrorCompatibility = <ThrowOnError extends boolean = false>(opt
|
|||
/**
|
||||
* Reorder backup schedules by providing an array of schedule short IDs in the desired order
|
||||
*/
|
||||
export const reorderBackupSchedules = <ThrowOnError extends boolean = false>(options?: Options<ReorderBackupSchedulesData, ThrowOnError>) => (options?.client ?? client).post<ReorderBackupSchedulesResponses, unknown, ThrowOnError>({
|
||||
export const reorderBackupSchedules = <ThrowOnError extends boolean = false>(options: Options<ReorderBackupSchedulesData, ThrowOnError>) => (options.client ?? client).post<ReorderBackupSchedulesResponses, unknown, ThrowOnError>({
|
||||
url: '/api/v1/backups/reorder',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -434,12 +434,12 @@ export const listNotificationDestinations = <ThrowOnError extends boolean = fals
|
|||
/**
|
||||
* Create a new notification destination
|
||||
*/
|
||||
export const createNotificationDestination = <ThrowOnError extends boolean = false>(options?: Options<CreateNotificationDestinationData, ThrowOnError>) => (options?.client ?? client).post<CreateNotificationDestinationResponses, unknown, ThrowOnError>({
|
||||
export const createNotificationDestination = <ThrowOnError extends boolean = false>(options: Options<CreateNotificationDestinationData, ThrowOnError>) => (options.client ?? client).post<CreateNotificationDestinationResponses, unknown, ThrowOnError>({
|
||||
url: '/api/v1/notifications/destinations',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -488,24 +488,24 @@ export const getRegistrationStatus = <ThrowOnError extends boolean = false>(opti
|
|||
/**
|
||||
* Update the registration status for new users. Requires global admin role.
|
||||
*/
|
||||
export const setRegistrationStatus = <ThrowOnError extends boolean = false>(options?: Options<SetRegistrationStatusData, ThrowOnError>) => (options?.client ?? client).put<SetRegistrationStatusResponses, unknown, ThrowOnError>({
|
||||
export const setRegistrationStatus = <ThrowOnError extends boolean = false>(options: Options<SetRegistrationStatusData, ThrowOnError>) => (options.client ?? client).put<SetRegistrationStatusResponses, unknown, ThrowOnError>({
|
||||
url: '/api/v1/system/registration-status',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Download the organization's Restic password for backup recovery. Requires organization owner or admin role and password re-authentication.
|
||||
*/
|
||||
export const downloadResticPassword = <ThrowOnError extends boolean = false>(options?: Options<DownloadResticPasswordData, ThrowOnError>) => (options?.client ?? client).post<DownloadResticPasswordResponses, unknown, ThrowOnError>({
|
||||
export const downloadResticPassword = <ThrowOnError extends boolean = false>(options: Options<DownloadResticPasswordData, ThrowOnError>) => (options.client ?? client).post<DownloadResticPasswordResponses, unknown, ThrowOnError>({
|
||||
url: '/api/v1/system/restic-password',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,7 +18,7 @@ export const AdvancedSection = ({ form }: AdvancedSectionProps) => {
|
|||
<FormControl>
|
||||
<Textarea
|
||||
{...field}
|
||||
placeholder="--iexclude-larger-than 500M --no-scan --read-concurrency 8"
|
||||
placeholder="--exclude-larger-than 500M --no-scan --read-concurrency 8"
|
||||
className="font-mono text-sm min-h-24"
|
||||
/>
|
||||
</FormControl>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useForm } from "react-hook-form";
|
|||
import { useScrollToFormError } from "~/client/hooks/use-scroll-to-form-error";
|
||||
import { Form } from "~/client/components/ui/form";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/client/components/ui/card";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "~/client/components/ui/collapsible";
|
||||
import type { BackupSchedule, Volume } from "~/client/lib/types";
|
||||
import { AdvancedSection } from "./advanced-section";
|
||||
import { BasicInfoSection } from "./basic-info-section";
|
||||
|
|
@ -175,15 +176,14 @@ export const CreateScheduleForm = ({ initialValues, formId, onSubmit, volume }:
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="min-w-0">
|
||||
<CardHeader>
|
||||
<CardTitle>Advanced</CardTitle>
|
||||
<CardDescription>
|
||||
Pass additional flags directly to the restic backup command. Use with caution.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<Card className="min-w-0 @container">
|
||||
<CardContent>
|
||||
<AdvancedSection form={form} />
|
||||
<Collapsible>
|
||||
<CollapsibleTrigger>Advanced</CollapsibleTrigger>
|
||||
<CollapsibleContent className="pb-4 space-y-4">
|
||||
<AdvancedSection form={form} />
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ export function ScheduleDetailsPage(props: Props) {
|
|||
updateSchedule.mutate({
|
||||
path: { shortId: schedule.shortId },
|
||||
body: {
|
||||
name: schedule.name,
|
||||
repositoryId: schedule.repositoryId,
|
||||
enabled,
|
||||
cronExpression: schedule.cronExpression,
|
||||
|
|
@ -200,6 +201,7 @@ export function ScheduleDetailsPage(props: Props) {
|
|||
excludePatterns: schedule.excludePatterns || [],
|
||||
excludeIfPresent: schedule.excludeIfPresent || [],
|
||||
oneFileSystem: schedule.oneFileSystem,
|
||||
customResticParams: schedule.customResticParams || [],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ALTER TABLE `backup_schedules_table` ADD `custom_restic_params` text DEFAULT '[]';
|
||||
ALTER TABLE `backup_schedules_table` ADD `custom_restic_params` text DEFAULT '[]';
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"id": "38a5fef6-d38e-4bf0-b9ab-0723db3741da",
|
||||
"prevIds": ["3a308c54-d950-464f-9490-fee06985fbeb"],
|
||||
"id": "1e14036e-978e-400a-bff0-fe379595fcbd",
|
||||
"prevIds": ["60a53b0c-395a-4960-9b48-1740388bd2dc"],
|
||||
"ddl": [
|
||||
{
|
||||
"name": "account",
|
||||
|
|
@ -48,6 +48,10 @@
|
|||
"name": "sessions_table",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "sso_provider",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "two_factor",
|
||||
"entityType": "tables"
|
||||
|
|
@ -1154,6 +1158,116 @@
|
|||
"entityType": "columns",
|
||||
"table": "sessions_table"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "provider_id",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "organization_id",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "user_id",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "issuer",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "domain",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "false",
|
||||
"generated": null,
|
||||
"name": "auto_link_matching_emails",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "oidc_config",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "saml_config",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch() * 1000)",
|
||||
"generated": null,
|
||||
"name": "created_at",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch() * 1000)",
|
||||
"generated": null,
|
||||
"name": "updated_at",
|
||||
"entityType": "columns",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
|
|
@ -1699,6 +1813,28 @@
|
|||
"entityType": "fks",
|
||||
"table": "sessions_table"
|
||||
},
|
||||
{
|
||||
"columns": ["organization_id"],
|
||||
"tableTo": "organization",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_sso_provider_organization_id_organization_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"columns": ["user_id"],
|
||||
"tableTo": "users_table",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "SET NULL",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_sso_provider_user_id_users_table_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"columns": ["user_id"],
|
||||
"tableTo": "users_table",
|
||||
|
|
@ -1798,6 +1934,13 @@
|
|||
"table": "sessions_table",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "sso_provider_pk",
|
||||
"table": "sso_provider",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
|
|
@ -2019,6 +2162,13 @@
|
|||
"entityType": "uniques",
|
||||
"table": "sessions_table"
|
||||
},
|
||||
{
|
||||
"columns": ["provider_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "sso_provider_provider_id_unique",
|
||||
"entityType": "uniques",
|
||||
"table": "sso_provider"
|
||||
},
|
||||
{
|
||||
"columns": ["username"],
|
||||
"nameExplicit": false,
|
||||
|
|
@ -10,6 +10,7 @@ import { generateShortId } from "~/server/utils/id";
|
|||
import { getOrganizationId } from "~/server/core/request-context";
|
||||
import { calculateNextRun } from "./backup.helpers";
|
||||
import { asShortId, type ShortId } from "~/server/utils/branded";
|
||||
import { validateCustomResticParams } from "~/server/utils/restic/helpers/validate-custom-params";
|
||||
|
||||
const listSchedules = async () => {
|
||||
const organizationId = getOrganizationId();
|
||||
|
|
@ -119,6 +120,11 @@ const createSchedule = async (data: CreateBackupScheduleBody) => {
|
|||
throw new NotFoundError("Repository not found");
|
||||
}
|
||||
|
||||
if (data.customResticParams && data.customResticParams.length > 0) {
|
||||
const paramError = validateCustomResticParams(data.customResticParams);
|
||||
if (paramError) throw new BadRequestError(paramError);
|
||||
}
|
||||
|
||||
const nextBackupAt = calculateNextRun(data.cronExpression);
|
||||
|
||||
const [newSchedule] = await db
|
||||
|
|
@ -167,6 +173,11 @@ const updateSchedule = async (scheduleIdOrShortId: number | string, data: Update
|
|||
throw new BadRequestError("Invalid cron expression");
|
||||
}
|
||||
|
||||
if (data.customResticParams && data.customResticParams.length > 0) {
|
||||
const paramError = validateCustomResticParams(data.customResticParams);
|
||||
if (paramError) throw new BadRequestError(paramError);
|
||||
}
|
||||
|
||||
if (data.name) {
|
||||
const existingName = await db.query.backupSchedulesTable.findFirst({
|
||||
where: {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { addCommonArgs } from "../helpers/add-common-args";
|
|||
import { buildEnv } from "../helpers/build-env";
|
||||
import { buildRepoUrl } from "../helpers/build-repo-url";
|
||||
import { cleanupTemporaryKeys } from "../helpers/cleanup-temporary-keys";
|
||||
import { validateCustomResticParams } from "../helpers/validate-custom-params";
|
||||
|
||||
export const backup = async (
|
||||
config: RepositoryConfig,
|
||||
|
|
@ -87,6 +88,10 @@ export const backup = async (
|
|||
}
|
||||
|
||||
if (options.customResticParams && options.customResticParams.length > 0) {
|
||||
const validationError = validateCustomResticParams(options.customResticParams);
|
||||
if (validationError) {
|
||||
throw new Error(`Invalid customResticParams: ${validationError}`);
|
||||
}
|
||||
for (const param of options.customResticParams) {
|
||||
const tokens = param.trim().split(/\s+/).filter(Boolean);
|
||||
args.push(...tokens);
|
||||
|
|
|
|||
71
app/server/utils/restic/helpers/validate-custom-params.ts
Normal file
71
app/server/utils/restic/helpers/validate-custom-params.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
const DENIED_FLAGS = new Set<string>([
|
||||
"--password-command",
|
||||
"--password-file",
|
||||
"--password",
|
||||
"-p",
|
||||
"--repository",
|
||||
"--repository-file",
|
||||
"-r",
|
||||
"--option",
|
||||
"-o",
|
||||
"--key-hint",
|
||||
"--tls-client-cert",
|
||||
"--cacert",
|
||||
"--repo",
|
||||
]);
|
||||
|
||||
const ALLOWED_FLAGS = new Set<string>([
|
||||
"--verbose",
|
||||
"-v",
|
||||
"--no-scan",
|
||||
"--exclude-larger-than",
|
||||
"--skip-if-unchanged",
|
||||
"--exclude-caches",
|
||||
"--force",
|
||||
"--use-fs-snapshot",
|
||||
"--read-concurrency",
|
||||
"--ignore-ctime",
|
||||
"--ignore-inode",
|
||||
"--with-atime",
|
||||
"--no-cache",
|
||||
"--cleanup-cache",
|
||||
"--cache-dir",
|
||||
"--limit-upload",
|
||||
"--limit-download",
|
||||
"--pack-size",
|
||||
"--dry-run",
|
||||
"-n",
|
||||
"--no-lock",
|
||||
"--json",
|
||||
]);
|
||||
|
||||
function extractFlagName(token: string): string | null {
|
||||
if (!token.startsWith("-")) {
|
||||
return null;
|
||||
}
|
||||
const eqIdx = token.indexOf("=");
|
||||
return eqIdx === -1 ? token : token.slice(0, eqIdx);
|
||||
}
|
||||
|
||||
export function validateCustomResticParams(params: string[]): string | null {
|
||||
for (const param of params) {
|
||||
const tokens = param.trim().split(/\s+/).filter(Boolean);
|
||||
|
||||
for (const token of tokens) {
|
||||
const flag = extractFlagName(token);
|
||||
if (flag === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DENIED_FLAGS.has(flag)) {
|
||||
return `Flag "${flag}" is not permitted in customResticParams`;
|
||||
}
|
||||
|
||||
if (!ALLOWED_FLAGS.has(flag)) {
|
||||
return `Unknown or unsupported flag "${flag}" in customResticParams. Permitted flags: ${[...ALLOWED_FLAGS].join(", ")}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -38,9 +38,5 @@ export default defineConfig({
|
|||
server: {
|
||||
host: "0.0.0.0",
|
||||
port: 3000,
|
||||
watch: {
|
||||
usePolling: true,
|
||||
ignored: ["**/data/**", "**/*.db", "**/*.db-journal", "**/*.db-wal", "**/*.db-shm"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue