@auth-tower/react
Version:
Everything you need to integrate Auth Tower in your react app
228 lines (227 loc) • 8.37 kB
JavaScript
'use client';
import { jsx as _jsx } from "react/jsx-runtime";
import { createContext, useContext, useState, useEffect } from 'react';
import { useAuthTowerSDK } from '../hooks/useAuthTowerSDK';
const TenantContext = createContext(undefined);
export function TenantProvider({ children, initialTenantId }) {
// Initialize SDK using the hook
const { sdk, isReady: sdkReady, isLoading: sdkLoading, error: sdkError } = useAuthTowerSDK({
tenantId: initialTenantId || undefined,
baseURL: "https://www.api.auth-tower.com",
});
const [currentTenant, setCurrentTenant] = useState(null);
const [tenants, setTenants] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
// Initialize and fetch tenants on mount, but only when SDK is ready
useEffect(() => {
if (sdkReady && sdk) {
refreshTenants();
}
}, [sdkReady, sdk]);
// Set initial tenant when tenants are loaded
useEffect(() => {
if (initialTenantId && tenants.length > 0 && !currentTenant && sdk) {
switchTenant(initialTenantId).catch(console.error);
}
}, [initialTenantId, tenants, currentTenant, sdk]);
const refreshTenants = async () => {
if (!sdk) {
setError('SDK not initialized');
return;
}
try {
setIsLoading(true);
setError(null);
const response = await sdk.getTenants();
setTenants(response.data || []);
}
catch (err) {
console.error('Failed to fetch tenants:', err);
setError('Failed to load tenants');
}
finally {
setIsLoading(false);
}
};
const switchTenant = async (tenantId) => {
if (!sdk) {
throw new Error('SDK not initialized');
}
try {
setError(null);
// Use SDK to switch tenant
await sdk.switchTenant(tenantId);
// Fetch complete tenant context with permissions and roles
const tenantContext = await sdk.getTenant(tenantId);
setCurrentTenant(tenantContext);
}
catch (err) {
console.error('Failed to switch tenant:', err);
setError('Failed to switch tenant');
throw err; // Re-throw to allow components to handle the error
}
};
/**
* Check if the current user has a specific permission for the current tenant
* @param permission - The permission to check (e.g., "user:read", "role:create")
* @returns boolean - true if user has the permission, false otherwise
*/
const has = (permission) => {
if (!currentTenant || !currentTenant.role || !currentTenant.role.permissions) {
console.warn('No current tenant or role permissions available for permission check');
return false;
}
// Check if the permission exists in the current tenant's role permissions
return currentTenant.role.permissions.some(p => p.name === permission);
};
/**
* Check if a feature is available based on subscription constraints
* @param featureName - The name of the feature to check
* @returns boolean - true if feature is available, false otherwise
*/
const hasFeature = (featureName) => {
if (!currentTenant || !currentTenant.constraints) {
return false;
}
const constraint = currentTenant.constraints[featureName];
if (constraint === undefined) {
return false;
}
// If constraint is boolean, return directly
if (typeof constraint === 'boolean') {
return constraint;
}
// If constraint is an object with enabled property
if (typeof constraint === 'object' && constraint !== null) {
return constraint.enabled === true;
}
return false;
};
/**
* Check if more items of a resource type can be added based on current usage and limits
* @param resourceType - The type of resource (e.g., "users", "roles", "permissions")
* @returns boolean - true if more can be added, false otherwise
*/
const canAddMore = (resourceType) => {
if (!currentTenant || !currentTenant.usage || !currentTenant.constraints) {
return false;
}
const currentUsage = getCurrentUsage(resourceType);
const limit = getLimit(resourceType);
// If no limit is set, assume unlimited
if (limit === null) {
return true;
}
return currentUsage < limit;
};
/**
* Get usage data for a specific resource type or all usage data
* @param resourceType - Optional: specific resource type to get usage for
* @returns usage data - number for specific resource, object for all usage
*/
const getUsage = (resourceType) => {
if (!currentTenant || !currentTenant.usage) {
return resourceType ? 0 : {};
}
if (resourceType) {
return currentTenant.usage[resourceType] || 0;
}
return currentTenant.usage;
};
/**
* Get constraint data for a specific feature or all constraints
* @param featureName - Optional: specific feature to get constraints for
* @returns constraint data - any for specific feature, object for all constraints
*/
const getConstraints = (featureName) => {
if (!currentTenant || !currentTenant.constraints) {
return featureName ? null : {};
}
if (featureName) {
return currentTenant.constraints[featureName] || null;
}
return currentTenant.constraints;
};
/**
* Get the current subscription ID
* @returns string | undefined - subscription ID if available
*/
const getSubscriptionId = () => {
return currentTenant === null || currentTenant === void 0 ? void 0 : currentTenant.subscription_id;
};
/**
* Get the current subscription class ID
* @returns string | undefined - subscription class ID if available
*/
const getSubscriptionClassId = () => {
return currentTenant === null || currentTenant === void 0 ? void 0 : currentTenant.subscription_class_id;
};
/**
* Get current usage count for a specific resource type
* @param resourceType - The type of resource
* @returns number - current usage count
*/
const getCurrentUsage = (resourceType) => {
if (!currentTenant || !currentTenant.usage) {
return 0;
}
const usage = currentTenant.usage[resourceType];
return typeof usage === 'number' ? usage : 0;
};
/**
* Get the limit for a specific resource type from constraints
* @param resourceType - The type of resource
* @returns number | null - limit if set, null if unlimited
*/
const getLimit = (resourceType) => {
if (!currentTenant || !currentTenant.constraints) {
return null;
}
const constraint = currentTenant.constraints[resourceType];
if (typeof constraint === 'number') {
return constraint;
}
if (typeof constraint === 'object' && constraint !== null) {
if (typeof constraint.limit === 'number') {
return constraint.limit;
}
if (typeof constraint.max === 'number') {
return constraint.max;
}
}
return null;
};
const contextValue = {
// SDK state
sdk,
sdkReady,
sdkLoading,
sdkError,
// Tenant state
currentTenant,
tenants,
isLoading,
error,
switchTenant,
refreshTenants,
has,
hasFeature,
canAddMore,
getUsage,
getConstraints,
getSubscriptionId,
getSubscriptionClassId,
getCurrentUsage,
getLimit,
};
return (_jsx(TenantContext.Provider, { value: contextValue, children: children }));
}
export function useTenant() {
const context = useContext(TenantContext);
if (context === undefined) {
throw new Error('useTenant must be used within a TenantProvider');
}
return context;
}
export default TenantContext;