chore: api client gen
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / checks (push) Has been cancelled
Release Workflow / e2e-tests (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled

This commit is contained in:
Nicolas Meienberger 2026-03-04 22:00:04 +01:00
parent e8ae8812ff
commit 6f560ecae9
8 changed files with 376 additions and 310 deletions

View file

@ -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);
/**

View file

@ -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;

View file

@ -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];
}
}

View file

@ -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
}
});

View file

@ -23,138 +23,6 @@ export type GetStatusResponses = {
export type GetStatusResponse = GetStatusResponses[keyof GetStatusResponses];
export type GetPublicSsoProvidersData = {
body?: never;
path?: never;
query?: never;
url: '/api/v1/auth/sso-providers';
};
export type GetPublicSsoProvidersResponses = {
/**
* List of public SSO providers
*/
200: {
providers: Array<{
organizationSlug: string;
providerId: string;
}>;
};
};
export type GetPublicSsoProvidersResponse = GetPublicSsoProvidersResponses[keyof GetPublicSsoProvidersResponses];
export type GetSsoSettingsData = {
body?: never;
path?: never;
query?: never;
url: '/api/v1/auth/sso-settings';
};
export type GetSsoSettingsResponses = {
/**
* SSO settings for the active organization
*/
200: {
invitations: Array<{
email: string;
expiresAt: string;
id: string;
role: string;
status: string;
}>;
providers: Array<{
autoLinkMatchingEmails: boolean;
domain: string;
issuer: string;
organizationId: string | null;
providerId: string;
type: string;
}>;
};
};
export type GetSsoSettingsResponse = GetSsoSettingsResponses[keyof GetSsoSettingsResponses];
export type DeleteSsoProviderData = {
body?: never;
path: {
providerId: string;
};
query?: never;
url: '/api/v1/auth/sso-providers/{providerId}';
};
export type DeleteSsoProviderErrors = {
/**
* Forbidden
*/
403: unknown;
/**
* Provider not found
*/
404: unknown;
};
export type DeleteSsoProviderResponses = {
/**
* SSO provider deleted successfully
*/
200: unknown;
};
export type UpdateSsoProviderAutoLinkingData = {
body?: {
enabled: boolean;
};
path: {
providerId: string;
};
query?: never;
url: '/api/v1/auth/sso-providers/{providerId}/auto-linking';
};
export type UpdateSsoProviderAutoLinkingErrors = {
/**
* Forbidden
*/
403: unknown;
/**
* Provider not found
*/
404: unknown;
};
export type UpdateSsoProviderAutoLinkingResponses = {
/**
* SSO provider auto-linking setting updated successfully
*/
200: unknown;
};
export type DeleteSsoInvitationData = {
body?: never;
path: {
invitationId: string;
};
query?: never;
url: '/api/v1/auth/sso-invitations/{invitationId}';
};
export type DeleteSsoInvitationErrors = {
/**
* Forbidden
*/
403: unknown;
};
export type DeleteSsoInvitationResponses = {
/**
* SSO invitation deleted successfully
*/
200: unknown;
};
export type GetAdminUsersData = {
body?: never;
path?: never;
@ -268,7 +136,7 @@ export type GetOrgMembersResponses = {
export type GetOrgMembersResponse = GetOrgMembersResponses[keyof GetOrgMembersResponses];
export type UpdateMemberRoleData = {
body?: {
body: {
role: 'admin' | 'member';
};
path: {
@ -323,6 +191,138 @@ export type RemoveOrgMemberResponses = {
200: unknown;
};
export type GetPublicSsoProvidersData = {
body?: never;
path?: never;
query?: never;
url: '/api/v1/auth/sso-providers';
};
export type GetPublicSsoProvidersResponses = {
/**
* List of public SSO providers
*/
200: {
providers: Array<{
organizationSlug: string;
providerId: string;
}>;
};
};
export type GetPublicSsoProvidersResponse = GetPublicSsoProvidersResponses[keyof GetPublicSsoProvidersResponses];
export type GetSsoSettingsData = {
body?: never;
path?: never;
query?: never;
url: '/api/v1/auth/sso-settings';
};
export type GetSsoSettingsResponses = {
/**
* SSO settings for the active organization
*/
200: {
invitations: Array<{
email: string;
expiresAt: string;
id: string;
role: string;
status: string;
}>;
providers: Array<{
autoLinkMatchingEmails: boolean;
domain: string;
issuer: string;
organizationId: string | null;
providerId: string;
type: string;
}>;
};
};
export type GetSsoSettingsResponse = GetSsoSettingsResponses[keyof GetSsoSettingsResponses];
export type DeleteSsoProviderData = {
body?: never;
path: {
providerId: string;
};
query?: never;
url: '/api/v1/auth/sso-providers/{providerId}';
};
export type DeleteSsoProviderErrors = {
/**
* Forbidden
*/
403: unknown;
/**
* Provider not found
*/
404: unknown;
};
export type DeleteSsoProviderResponses = {
/**
* SSO provider deleted successfully
*/
200: unknown;
};
export type UpdateSsoProviderAutoLinkingData = {
body: {
enabled: boolean;
};
path: {
providerId: string;
};
query?: never;
url: '/api/v1/auth/sso-providers/{providerId}/auto-linking';
};
export type UpdateSsoProviderAutoLinkingErrors = {
/**
* Forbidden
*/
403: unknown;
/**
* Provider not found
*/
404: unknown;
};
export type UpdateSsoProviderAutoLinkingResponses = {
/**
* SSO provider auto-linking setting updated successfully
*/
200: unknown;
};
export type DeleteSsoInvitationData = {
body?: never;
path: {
invitationId: string;
};
query?: never;
url: '/api/v1/auth/sso-invitations/{invitationId}';
};
export type DeleteSsoInvitationErrors = {
/**
* Forbidden
*/
403: unknown;
};
export type DeleteSsoInvitationResponses = {
/**
* SSO invitation deleted successfully
*/
200: unknown;
};
export type ListVolumesData = {
body?: never;
path?: never;
@ -399,7 +399,7 @@ export type ListVolumesResponses = {
export type ListVolumesResponse = ListVolumesResponses[keyof ListVolumesResponses];
export type CreateVolumeData = {
body?: {
body: {
config: {
backend: 'directory';
path: string;
@ -524,7 +524,7 @@ export type CreateVolumeResponses = {
export type CreateVolumeResponse = CreateVolumeResponses[keyof CreateVolumeResponses];
export type TestConnectionData = {
body?: {
body: {
config: {
backend: 'directory';
path: string;
@ -703,7 +703,7 @@ export type GetVolumeResponses = {
export type GetVolumeResponse = GetVolumeResponses[keyof GetVolumeResponses];
export type UpdateVolumeData = {
body?: {
body: {
autoRemount?: boolean;
config?: {
backend: 'directory';
@ -1168,7 +1168,7 @@ export type ListRepositoriesResponses = {
export type ListRepositoriesResponse = ListRepositoriesResponses[keyof ListRepositoriesResponses];
export type CreateRepositoryData = {
body?: {
body: {
config: {
accessKeyId: string;
backend: 'r2';
@ -1588,7 +1588,7 @@ export type GetRepositoryResponses = {
export type GetRepositoryResponse = GetRepositoryResponses[keyof GetRepositoryResponses];
export type UpdateRepositoryData = {
body?: {
body: {
compressionMode?: 'auto' | 'max' | 'off';
config?: {
accessKeyId: string;
@ -1986,7 +1986,7 @@ export type GetRepositoryStatsResponses = {
export type GetRepositoryStatsResponse = GetRepositoryStatsResponses[keyof GetRepositoryStatsResponses];
export type DeleteSnapshotsData = {
body?: {
body: {
snapshotIds: Array<string>;
};
path: {
@ -2208,7 +2208,7 @@ export type DumpSnapshotResponses = {
export type DumpSnapshotResponse = DumpSnapshotResponses[keyof DumpSnapshotResponses];
export type RestoreSnapshotData = {
body?: {
body: {
snapshotId: string;
delete?: boolean;
exclude?: Array<string>;
@ -2315,7 +2315,7 @@ export type UnlockRepositoryResponses = {
export type UnlockRepositoryResponse = UnlockRepositoryResponses[keyof UnlockRepositoryResponses];
export type TagSnapshotsData = {
body?: {
body: {
snapshotIds: Array<string>;
add?: Array<string>;
remove?: Array<string>;
@ -2340,7 +2340,7 @@ export type TagSnapshotsResponses = {
export type TagSnapshotsResponse = TagSnapshotsResponses[keyof TagSnapshotsResponses];
export type DevPanelExecData = {
body?: {
body: {
command: string;
args?: Array<string>;
};
@ -2381,6 +2381,7 @@ export type ListBackupSchedulesResponses = {
200: Array<{
createdAt: number;
cronExpression: string;
customResticParams: Array<string> | null;
enabled: boolean;
excludeIfPresent: Array<string> | null;
excludePatterns: Array<string> | null;
@ -2651,12 +2652,13 @@ export type ListBackupSchedulesResponses = {
export type ListBackupSchedulesResponse = ListBackupSchedulesResponses[keyof ListBackupSchedulesResponses];
export type CreateBackupScheduleData = {
body?: {
body: {
cronExpression: string;
enabled: boolean;
name: string;
repositoryId: string;
volumeId: number | string;
customResticParams?: Array<string>;
excludeIfPresent?: Array<string>;
excludePatterns?: Array<string>;
includePatterns?: Array<string>;
@ -2684,6 +2686,7 @@ export type CreateBackupScheduleResponses = {
201: {
createdAt: number;
cronExpression: string;
customResticParams: Array<string> | null;
enabled: boolean;
excludeIfPresent: Array<string> | null;
excludePatterns: Array<string> | null;
@ -2749,6 +2752,7 @@ export type GetBackupScheduleResponses = {
200: {
createdAt: number;
cronExpression: string;
customResticParams: Array<string> | null;
enabled: boolean;
excludeIfPresent: Array<string> | null;
excludePatterns: Array<string> | null;
@ -3019,9 +3023,10 @@ export type GetBackupScheduleResponses = {
export type GetBackupScheduleResponse = GetBackupScheduleResponses[keyof GetBackupScheduleResponses];
export type UpdateBackupScheduleData = {
body?: {
body: {
cronExpression: string;
repositoryId: string;
customResticParams?: Array<string>;
enabled?: boolean;
excludeIfPresent?: Array<string>;
excludePatterns?: Array<string>;
@ -3053,6 +3058,7 @@ export type UpdateBackupScheduleResponses = {
200: {
createdAt: number;
cronExpression: string;
customResticParams: Array<string> | null;
enabled: boolean;
excludeIfPresent: Array<string> | null;
excludePatterns: Array<string> | null;
@ -3098,6 +3104,7 @@ export type GetBackupScheduleForVolumeResponses = {
200: {
createdAt: number;
cronExpression: string;
customResticParams: Array<string> | null;
enabled: boolean;
excludeIfPresent: Array<string> | null;
excludePatterns: Array<string> | null;
@ -3529,7 +3536,7 @@ export type GetScheduleNotificationsResponses = {
export type GetScheduleNotificationsResponse = GetScheduleNotificationsResponses[keyof GetScheduleNotificationsResponses];
export type UpdateScheduleNotificationsData = {
body?: {
body: {
assignments: Array<{
destinationId: number;
notifyOnFailure: boolean;
@ -3837,7 +3844,7 @@ export type GetScheduleMirrorsResponses = {
export type GetScheduleMirrorsResponse = GetScheduleMirrorsResponses[keyof GetScheduleMirrorsResponses];
export type UpdateScheduleMirrorsData = {
body?: {
body: {
mirrors: Array<{
enabled: boolean;
repositoryId: string;
@ -4070,7 +4077,7 @@ export type GetMirrorCompatibilityResponses = {
export type GetMirrorCompatibilityResponse = GetMirrorCompatibilityResponses[keyof GetMirrorCompatibilityResponses];
export type ReorderBackupSchedulesData = {
body?: {
body: {
scheduleShortIds: Array<string>;
};
path?: never;
@ -4202,7 +4209,7 @@ export type ListNotificationDestinationsResponses = {
export type ListNotificationDestinationsResponse = ListNotificationDestinationsResponses[keyof ListNotificationDestinationsResponses];
export type CreateNotificationDestinationData = {
body?: {
body: {
config: {
apiToken: string;
priority: -1 | 0 | 1;
@ -4466,7 +4473,7 @@ export type GetNotificationDestinationResponses = {
export type GetNotificationDestinationResponse = GetNotificationDestinationResponses[keyof GetNotificationDestinationResponses];
export type UpdateNotificationDestinationData = {
body?: {
body: {
config?: {
apiToken: string;
priority: -1 | 0 | 1;
@ -4721,7 +4728,7 @@ export type GetRegistrationStatusResponses = {
export type GetRegistrationStatusResponse = GetRegistrationStatusResponses[keyof GetRegistrationStatusResponses];
export type SetRegistrationStatusData = {
body?: {
body: {
enabled: boolean;
};
path?: never;
@ -4741,7 +4748,7 @@ export type SetRegistrationStatusResponses = {
export type SetRegistrationStatusResponse = SetRegistrationStatusResponses[keyof SetRegistrationStatusResponses];
export type DownloadResticPasswordData = {
body?: {
body: {
password: string;
};
path?: never;

View file

@ -83,14 +83,23 @@ export const auth = betterAuth({
return { data: user };
},
after: async (user, ctx) => {
if (ssoIntegration.isSsoCallback(ctx)) {
await ssoIntegration.onUserCreated(user, ctx);
}
},
},
},
session: {
create: {
before: async (session, ctx) => {
const membership =
(await ssoIntegration.resolveOrgMembership(session.userId, ctx)) ??
(await ensureDefaultOrg(session.userId));
if (ssoIntegration.isSsoCallback(ctx)) {
const membership = await ssoIntegration.resolveOrgMembershipOrThrow(session.userId, ctx);
return { data: { ...session, activeOrganizationId: membership.organizationId } };
}
const membership = await ensureDefaultOrg(session.userId);
return { data: { ...session, activeOrganizationId: membership.organizationId } };
},
},

View file

@ -4,6 +4,7 @@ import { eq } from "drizzle-orm";
import { db } from "~/server/db/db";
import { account, invitation, member, organization, ssoProvider, usersTable } from "~/server/db/schema";
import { ssoIntegration } from "../sso.integration";
import { ensureDefaultOrg } from "~/server/lib/auth/helpers/create-default-org";
function createMockSsoCallbackContext(providerId: string): GenericEndpointContext {
return {
@ -280,4 +281,51 @@ describe("ssoIntegration.resolveOrgMembership", () => {
const ctx = createMockSsoCallbackContext("oidc-org-b");
await expect(ssoIntegration.resolveOrgMembership(userId, ctx)).rejects.toThrow("invite-only");
});
test("keeps invited org assignment when session context loses providerId after user creation", async () => {
const userId = await createUser("alice@example.com", randomSlug("alice"));
const inviterId = await createUser("inviter@example.com", randomSlug("inviter"));
const invitedOrgId = randomId();
await db.insert(organization).values({
id: invitedOrgId,
name: "Acme Corp",
slug: randomSlug("acme"),
createdAt: new Date(),
});
await db.insert(ssoProvider).values({
id: randomId(),
providerId: "oidc-acme",
organizationId: invitedOrgId,
userId: inviterId,
issuer: "https://issuer.example.com",
domain: "example.com",
});
await db.insert(invitation).values({
id: randomId(),
organizationId: invitedOrgId,
email: "alice@example.com",
role: "member",
status: "pending",
expiresAt: new Date(Date.now() + 60 * 60 * 1000),
createdAt: new Date(),
inviterId,
});
const user = await db.query.usersTable.findFirst({ where: { id: userId } });
if (!user) {
throw new Error("Expected user to exist");
}
await ssoIntegration.onUserCreated(user, createMockSsoCallbackContext("oidc-acme"));
const membership = await ensureDefaultOrg(userId);
expect(membership.organizationId).toBe(invitedOrgId);
const [updatedInvitation] = await db.select().from(invitation).where(eq(invitation.organizationId, invitedOrgId));
expect(updatedInvitation.status).toBe("accepted");
});
});

View file

@ -82,6 +82,21 @@ async function onUserCreate(
user.hasDownloadedResticPassword = true;
}
async function resolveOrgMembershipOrThrow(userId: string, ctx: GenericEndpointContext | null) {
const membership = await resolveOrgMembership(userId, ctx);
if (!membership) {
throw new APIError("BAD_REQUEST", {
message: "Unable to resolve organization membership for this SSO session",
});
}
return membership;
}
async function onUserCreated(user: User, ctx: GenericEndpointContext | null) {
await resolveOrgMembershipOrThrow(user.id, ctx);
}
export const ssoIntegration = {
plugin: sso({
trustEmailVerified: false,
@ -101,6 +116,10 @@ export const ssoIntegration = {
onUserCreate,
onUserCreated,
resolveOrgMembershipOrThrow,
resolveOrgMembership,
resolveTrustedProviders: resolveTrustedProvidersForRequest,