import { handleApiError, rbacApi, type AccessRecord, type Role, type UserRole, } from "@/main-axios"; import type { AuthOverrideProtocol } from "@/types/auth-protocols"; import { getConnectedRemoteApi, resolveRemoteHostId, } from "@/lib/remote-server-api"; async function getSharingTarget(hostId: number, syncId?: string | null) { const api = await getConnectedRemoteApi(); if (!api || !syncId) return { api: rbacApi, hostId }; const remoteHostId = await resolveRemoteHostId(syncId); if (remoteHostId === null) { throw new Error("The synced host does not exist on the remote server"); } return { api, hostId: remoteHostId }; } export async function getRoles(): Promise<{ roles: Role[] }> { try { const api = (await getConnectedRemoteApi()) ?? rbacApi; const response = await api.get("/rbac/roles"); return response.data; } catch (error) { throw handleApiError(error, "fetch roles"); } } export async function createRole(roleData: { name: string; displayName: string; description?: string | null; }): Promise<{ role: Role }> { try { const response = await rbacApi.post("/rbac/roles", roleData); return response.data; } catch (error) { throw handleApiError(error, "create role"); } } export async function updateRole( roleId: number, roleData: { displayName?: string; description?: string | null; permissions?: string[]; }, ): Promise<{ role: Role }> { try { const response = await rbacApi.put(`/rbac/roles/${roleId}`, roleData); return response.data; } catch (error) { throw handleApiError(error, "update role"); } } export async function deleteRole( roleId: number, ): Promise<{ success: boolean }> { try { const response = await rbacApi.delete(`/rbac/roles/${roleId}`); return response.data; } catch (error) { throw handleApiError(error, "delete role"); } } export async function getUserRoles( userId: string, ): Promise<{ roles: UserRole[] }> { try { const response = await rbacApi.get(`/rbac/users/${userId}/roles`); return response.data; } catch (error) { throw handleApiError(error, "fetch user roles"); } } export async function assignRoleToUser( userId: string, roleId: number, ): Promise<{ success: boolean }> { try { const response = await rbacApi.post(`/rbac/users/${userId}/roles`, { roleId, }); return response.data; } catch (error) { throw handleApiError(error, "assign role to user"); } } export async function removeRoleFromUser( userId: string, roleId: number, ): Promise<{ success: boolean }> { try { const response = await rbacApi.delete( `/rbac/users/${userId}/roles/${roleId}`, ); return response.data; } catch (error) { throw handleApiError(error, "remove role from user"); } } export type SharePermissionLevel = "connect" | "view" | "edit" | "manage"; export interface ShareTarget { type: "user" | "role"; id: string | number; } export async function shareHost( hostId: number, shareData: { targets: ShareTarget[]; permissionLevel: SharePermissionLevel; durationHours?: number; }, syncId?: string | null, ): Promise<{ success: boolean; expiresAt: string | null; results: Array<{ type: "user" | "role"; id: string | number; accessId: number; created: boolean; }>; }> { try { const target = await getSharingTarget(hostId, syncId); const response = await target.api.post( `/rbac/host/${target.hostId}/share`, shareData, ); return response.data; } catch (error) { throw handleApiError(error, "share host"); } } export async function shareFolder( folder: string, shareData: { targets: ShareTarget[]; permissionLevel: SharePermissionLevel; durationHours?: number; }, ): Promise<{ success: boolean; expiresAt: string | null; hostsShared: number; hostsTotal: number; hostResults: Array<{ hostId: number; shared: boolean; reason?: string }>; }> { try { const api = (await getConnectedRemoteApi()) ?? rbacApi; const response = await api.post("/rbac/folder/share", { folder, ...shareData, }); return response.data; } catch (error) { throw handleApiError(error, "share folder"); } } export async function updateHostAccess( hostId: number, accessId: number, update: { permissionLevel?: SharePermissionLevel; durationHours?: number | null; }, syncId?: string | null, ): Promise<{ success: boolean; expiresAt: string | null }> { try { const target = await getSharingTarget(hostId, syncId); const response = await target.api.patch( `/rbac/host/${target.hostId}/access/${accessId}`, update, ); return response.data; } catch (error) { throw handleApiError(error, "update host access"); } } export async function getHostAccess( hostId: number, syncId?: string | null, ): Promise<{ accessList: AccessRecord[]; isOwner?: boolean }> { try { const target = await getSharingTarget(hostId, syncId); const response = await target.api.get(`/rbac/host/${target.hostId}/access`); return response.data; } catch (error) { throw handleApiError(error, "fetch host access"); } } export interface PermissionCatalogEntry { group: string; permissions: string[]; } export async function getPermissionsCatalog(): Promise<{ catalog: PermissionCatalogEntry[]; }> { try { const response = await rbacApi.get("/rbac/permissions/catalog"); return response.data; } catch (error) { throw handleApiError(error, "fetch permissions catalog"); } } export async function getSharedHosts(): Promise<{ sharedHosts: Array<{ id: number; name: string | null; ip: string; port: number; username: string; folder: string | null; tags: string | null; permissionLevel: SharePermissionLevel; expiresAt: string | null; grantedBy: string; ownerUsername: string; }>; }> { try { const api = (await getConnectedRemoteApi()) ?? rbacApi; const response = await api.get("/rbac/shared-hosts"); return response.data; } catch (error) { throw handleApiError(error, "fetch shared hosts"); } } export async function revokeHostAccess( hostId: number, accessId: number, syncId?: string | null, ): Promise<{ success: boolean }> { try { const target = await getSharingTarget(hostId, syncId); const response = await target.api.delete( `/rbac/host/${target.hostId}/access/${accessId}`, ); return response.data; } catch (error) { throw handleApiError(error, "revoke host access"); } } export async function getHostAuthOverride( hostId: number, protocol: AuthOverrideProtocol, ): Promise<{ protocol: AuthOverrideProtocol; credentialId: number | null }> { try { const response = await rbacApi.get( `/rbac/host-access/${hostId}/auth/${protocol}`, ); return response.data; } catch (error) { throw handleApiError(error, "fetch host authentication override"); } } export async function setHostAuthOverride( hostId: number, protocol: AuthOverrideProtocol, credentialId: number | null, ): Promise<{ success: boolean; protocol: AuthOverrideProtocol; credentialId: number | null; }> { try { const response = await rbacApi.put( `/rbac/host-access/${hostId}/auth/${protocol}`, { credentialId }, ); return response.data; } catch (error) { throw handleApiError(error, "update host authentication override"); } } // ============================================================================ // SNIPPET SHARING // ============================================================================ export async function shareSnippet( snippetId: number, shareData: { targetType: "user" | "role"; targetUserId?: string; targetRoleId?: number; durationHours?: number; }, ): Promise<{ success: boolean }> { try { const response = await rbacApi.post( `/rbac/snippet/${snippetId}/share`, shareData, ); return response.data; } catch (error) { throw handleApiError(error, "share snippet"); } } export async function getSnippetAccess( snippetId: number, ): Promise<{ accessList: AccessRecord[] }> { try { const response = await rbacApi.get(`/rbac/snippet/${snippetId}/access`); return response.data; } catch (error) { throw handleApiError(error, "fetch snippet access"); } } export async function revokeSnippetAccess( snippetId: number, accessId: number, ): Promise<{ success: boolean }> { try { const response = await rbacApi.delete( `/rbac/snippet/${snippetId}/access/${accessId}`, ); return response.data; } catch (error) { throw handleApiError(error, "revoke snippet access"); } } export async function getSharedSnippets(): Promise<{ sharedSnippets: Array<{ id: number; name: string; content: string; description: string | null; folder: string | null; ownerUsername: string; permissionLevel: string; expiresAt: string | null; }>; }> { try { const response = await rbacApi.get("/rbac/shared-snippets"); return response.data; } catch (error) { handleApiError(error, "fetch shared snippets"); } }