add-enterprise-feature
Access: /template add-enterprise-feature or /t add-enterprise-feature
Creates advanced enterprise-grade features for your MakerKit application, including SSO, audit logs, advanced permissions, and compliance tools.
What It Does
The add-enterprise-feature command helps you create:
- Single Sign-On (SSO) with SAML/OAuth
- Advanced audit logging and compliance
- Role-based access control (RBAC)
- Data export and retention policies
- API rate limiting and quotas
- Multi-region deployment support
- Advanced security features
Usage
bash
/template add-enterprise-feature "Description"
# or
/t add-enterprise-feature "Description"When prompted, specify:
- Feature type (SSO, audit logs, RBAC, etc.)
- Compliance requirements (SOC2, GDPR, HIPAA)
- Security level needed
- Integration requirements
Prerequisites
- A MakerKit project with teams functionality
- Understanding of enterprise requirements
- Commercial MakerKit license from MakerKit
What Gets Created
1. Single Sign-On (SSO)
SAML 2.0 implementation:
typescript
// lib/sso/saml-provider.ts
import { SAML } from '@node-saml/node-saml';
import { createHash } from 'crypto';
export class SAMLProvider {
private saml: SAML;
constructor(config: SAMLConfig) {
this.saml = new SAML({
callbackUrl: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/saml/callback`,
entryPoint: config.entryPoint,
issuer: config.issuer,
cert: config.certificate,
privateKey: process.env.SAML_PRIVATE_KEY,
identifierFormat: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
signatureAlgorithm: 'sha256',
digestAlgorithm: 'sha256',
});
}
async generateLoginUrl(accountId: string): Promise<string> {
const request = await this.saml.createLoginRequestAsync(
accountId,
{ additionalParams: { accountId } }
);
return request;
}
async validateResponse(samlResponse: string): Promise<SSOUser> {
const profile = await this.saml.validatePostResponseAsync(samlResponse);
if (!profile) {
throw new Error('Invalid SAML response');
}
return {
id: profile.nameID,
email: profile.email || profile.mail,
firstName: profile.givenName,
lastName: profile.sn,
attributes: profile.attributes,
};
}
}
// SSO configuration table
create table public.sso_configurations (
id uuid default gen_random_uuid() primary key,
account_id uuid not null references public.accounts(id) on delete cascade,
provider text not null check (provider in ('saml', 'oidc')),
enabled boolean default false,
metadata jsonb not null,
created_at timestamptz default now(),
updated_at timestamptz default now(),
unique(account_id, provider)
);
-- SSO domains for auto-detection
create table public.sso_domains (
id uuid default gen_random_uuid() primary key,
account_id uuid not null references public.accounts(id) on delete cascade,
domain text not null unique,
verified boolean default false,
verification_token text,
created_at timestamptz default now()
);2. Advanced Audit Logging
Comprehensive audit trail:
typescript
// lib/audit/audit-logger.ts
export interface AuditEvent {
actor: {
id: string;
type: 'user' | 'system' | 'api';
email?: string;
ip?: string;
userAgent?: string;
};
action: string;
resource: {
type: string;
id: string;
name?: string;
};
changes?: {
before: any;
after: any;
};
metadata?: Record<string, any>;
timestamp: Date;
}
export class AuditLogger {
private queue: AuditEvent[] = [];
private flushInterval: NodeJS.Timer;
constructor() {
// Batch write events every 5 seconds
this.flushInterval = setInterval(() => this.flush(), 5000);
}
async log(event: AuditEvent) {
// Add context
const enrichedEvent = {
...event,
id: generateId(),
timestamp: new Date(),
environment: process.env.NODE_ENV,
version: process.env.APP_VERSION,
};
this.queue.push(enrichedEvent);
// Immediate flush for critical events
if (this.isCriticalEvent(event)) {
await this.flush();
}
}
private async flush() {
if (this.queue.length === 0) return;
const events = [...this.queue];
this.queue = [];
try {
await this.writeToDatabase(events);
await this.writeToSIEM(events);
} catch (error) {
// Re-queue failed events
this.queue.unshift(...events);
console.error('Audit log flush failed:', error);
}
}
private async writeToDatabase(events: AuditEvent[]) {
const { error } = await supabase
.from('audit_logs')
.insert(events.map(event => ({
actor_id: event.actor.id,
actor_type: event.actor.type,
actor_email: event.actor.email,
action: event.action,
resource_type: event.resource.type,
resource_id: event.resource.id,
resource_name: event.resource.name,
changes: event.changes,
metadata: event.metadata,
ip_address: event.actor.ip,
user_agent: event.actor.userAgent,
timestamp: event.timestamp,
})));
if (error) throw error;
}
private async writeToSIEM(events: AuditEvent[]) {
// Send to external SIEM system
if (process.env.SIEM_ENDPOINT) {
await fetch(process.env.SIEM_ENDPOINT, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SIEM_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ events }),
});
}
}
}
// Audit log viewer component
export function AuditLogViewer({ accountId }: { accountId: string }) {
const { data: logs, isLoading } = useAuditLogs(accountId);
return (
<div className="space-y-4">
<div className="flex justify-between items-center">
<h2 className="text-xl font-semibold">Audit Log</h2>
<Button onClick={exportAuditLogs}>
Export CSV
</Button>
</div>
<DataTable
columns={auditLogColumns}
data={logs || []}
loading={isLoading}
searchKey="action"
filters={[
{
column: 'actor_type',
title: 'Actor Type',
options: ['user', 'system', 'api'],
},
{
column: 'resource_type',
title: 'Resource',
options: ['user', 'team', 'project', 'billing'],
},
]}
/>
</div>
);
}3. Role-Based Access Control
Granular permissions system:
typescript
// lib/rbac/permissions.ts
export const PERMISSIONS = {
// User management
'users:read': 'View users',
'users:write': 'Create and edit users',
'users:delete': 'Delete users',
'users:impersonate': 'Impersonate users',
// Team management
'teams:read': 'View team details',
'teams:write': 'Edit team settings',
'teams:delete': 'Delete team',
'teams:manage_members': 'Add/remove team members',
'teams:manage_roles': 'Manage team roles',
// Billing
'billing:read': 'View billing information',
'billing:write': 'Update payment methods',
'billing:manage_subscription': 'Change subscription plans',
// Security
'security:manage_sso': 'Configure SSO',
'security:manage_2fa': 'Enforce 2FA policies',
'security:view_audit_logs': 'View audit logs',
'security:manage_api_keys': 'Create/revoke API keys',
// Data
'data:export': 'Export data',
'data:delete': 'Delete data',
'data:retention': 'Manage data retention',
} as const;
// Custom roles
export interface Role {
id: string;
name: string;
description: string;
permissions: string[];
isSystem: boolean;
}
// Database schema
create table public.custom_roles (
id uuid default gen_random_uuid() primary key,
account_id uuid not null references public.accounts(id) on delete cascade,
name text not null,
description text,
permissions text[] not null default '{}',
is_system boolean default false,
created_at timestamptz default now(),
updated_at timestamptz default now(),
unique(account_id, name)
);
create table public.user_custom_roles (
user_id uuid not null references auth.users(id) on delete cascade,
account_id uuid not null references public.accounts(id) on delete cascade,
role_id uuid not null references public.custom_roles(id) on delete cascade,
granted_by uuid references auth.users(id),
granted_at timestamptz default now(),
primary key (user_id, account_id, role_id)
);
// Permission checking
export async function checkPermission(
userId: string,
accountId: string,
permission: keyof typeof PERMISSIONS
): Promise<boolean> {
// Check system roles first
const systemRole = await getUserSystemRole(userId, accountId);
if (systemRole === 'owner') return true;
// Check custom roles
const { data: customRoles } = await supabase
.from('user_custom_roles')
.select('role:custom_roles(permissions)')
.eq('user_id', userId)
.eq('account_id', accountId);
const allPermissions = customRoles?.flatMap(r => r.role.permissions) || [];
return allPermissions.includes(permission);
}
// Permission guard component
export function RequirePermission({
permission,
fallback = <AccessDenied />,
children,
}: {
permission: keyof typeof PERMISSIONS;
fallback?: React.ReactNode;
children: React.ReactNode;
}) {
const { user } = useUser();
const { account } = useAccount();
const { data: hasPermission } = usePermission(permission);
if (!hasPermission) {
return <>{fallback}</>;
}
return <>{children}</>;
}4. Data Export & Compliance
GDPR and compliance tools:
typescript
// lib/compliance/data-export.ts
export class DataExporter {
async exportUserData(userId: string): Promise<ExportBundle> {
const bundle: ExportBundle = {
metadata: {
userId,
exportDate: new Date(),
version: '1.0',
},
data: {},
};
// Export from all tables
const tables = [
'users',
'accounts_account_members',
'projects',
'documents',
'audit_logs',
'subscriptions',
];
for (const table of tables) {
bundle.data[table] = await this.exportTableData(table, userId);
}
// Export from external services
bundle.data.stripe = await this.exportStripeData(userId);
bundle.data.emails = await this.exportEmailData(userId);
// Generate signed archive
const archive = await this.createSecureArchive(bundle);
// Log export event
await auditLogger.log({
actor: { id: userId, type: 'user' },
action: 'data.exported',
resource: { type: 'user', id: userId },
metadata: { tables: Object.keys(bundle.data) },
});
return archive;
}
private async createSecureArchive(bundle: ExportBundle): Promise<Buffer> {
const json = JSON.stringify(bundle, null, 2);
const encrypted = await encrypt(json, process.env.EXPORT_ENCRYPTION_KEY!);
const zip = new AdmZip();
zip.addFile('data.json.enc', Buffer.from(encrypted));
zip.addFile('README.txt', Buffer.from(
'This is your personal data export.\n' +
'The data.json.enc file is encrypted.\n' +
'Contact support for decryption assistance.'
));
return zip.toBuffer();
}
}
// Data retention policies
export async function applyRetentionPolicies() {
const policies = await getRetentionPolicies();
for (const policy of policies) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - policy.retentionDays);
if (policy.action === 'delete') {
await supabase
.from(policy.table)
.delete()
.lt('created_at', cutoffDate.toISOString());
} else if (policy.action === 'anonymize') {
await anonymizeOldData(policy.table, cutoffDate);
}
}
}5. API Rate Limiting
Enterprise API management:
typescript
// lib/api/rate-limiter.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
// Configure rate limits by plan
const RATE_LIMITS = {
free: { requests: 100, window: '1h' },
pro: { requests: 1000, window: '1h' },
enterprise: { requests: 10000, window: '1h' },
custom: { requests: -1, window: '1h' }, // Unlimited
};
export function createRateLimiter(tier: keyof typeof RATE_LIMITS) {
const limit = RATE_LIMITS[tier];
if (limit.requests === -1) {
return { limit: async () => ({ success: true }) };
}
return new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(limit.requests, limit.window),
analytics: true,
});
}
// API middleware
export async function rateLimitMiddleware(
request: NextRequest,
accountId: string
) {
const account = await getAccount(accountId);
const rateLimiter = createRateLimiter(account.plan);
const identifier = `${accountId}:${request.ip}`;
const { success, limit, reset, remaining } = await rateLimiter.limit(
identifier
);
// Add rate limit headers
const response = NextResponse.next();
response.headers.set('X-RateLimit-Limit', limit.toString());
response.headers.set('X-RateLimit-Remaining', remaining.toString());
response.headers.set('X-RateLimit-Reset', new Date(reset).toISOString());
if (!success) {
return new NextResponse('Rate limit exceeded', {
status: 429,
headers: response.headers,
});
}
return response;
}
// Usage analytics
export async function getAPIUsageAnalytics(accountId: string) {
const analytics = await redis.get(`analytics:${accountId}`);
return {
totalRequests: analytics?.totalRequests || 0,
requestsByEndpoint: analytics?.endpoints || {},
requestsByDay: analytics?.daily || {},
averageLatency: analytics?.avgLatency || 0,
errorRate: analytics?.errorRate || 0,
};
}6. Multi-Region Support
Deploy across regions:
typescript
// lib/infrastructure/multi-region.ts
export const REGIONS = {
'us-east-1': { name: 'US East', endpoint: 'https://us-east.api.app.com' },
'eu-west-1': { name: 'EU West', endpoint: 'https://eu-west.api.app.com' },
'ap-southeast-1': { name: 'Asia Pacific', endpoint: 'https://ap.api.app.com' },
};
// Data residency configuration
create table public.data_residency (
account_id uuid primary key references public.accounts(id) on delete cascade,
primary_region text not null,
allowed_regions text[] not null default '{}',
data_processing_agreement boolean default false,
created_at timestamptz default now()
);
// Region-aware database client
export function getRegionalClient(accountId: string) {
const residency = await getDataResidency(accountId);
const region = residency?.primary_region || 'us-east-1';
return createClient(
process.env[`SUPABASE_URL_${region.toUpperCase()}`],
process.env.SUPABASE_SERVICE_ROLE_KEY,
{
global: {
headers: {
'x-region': region,
},
},
}
);
}7. Advanced Security Features
Enterprise security controls:
typescript
// lib/security/advanced-features.ts
// IP Allowlisting
export async function checkIPAllowlist(
accountId: string,
ipAddress: string
): Promise<boolean> {
const { data: allowlist } = await supabase
.from('ip_allowlists')
.select('cidr_blocks')
.eq('account_id', accountId)
.eq('enabled', true);
if (!allowlist || allowlist.length === 0) {
return true; // No restrictions
}
return allowlist.some(entry =>
isIPInCIDR(ipAddress, entry.cidr_blocks)
);
}
// Session management
export async function enforceSessionPolicies(
userId: string,
accountId: string
) {
const policies = await getSecurityPolicies(accountId);
if (policies.maxConcurrentSessions) {
const sessions = await getActiveSessions(userId);
if (sessions.length >= policies.maxConcurrentSessions) {
// Terminate oldest session
await terminateSession(sessions[0].id);
}
}
if (policies.sessionTimeout) {
// Set session expiry
await setSessionExpiry(userId, policies.sessionTimeout);
}
}
// Anomaly detection
export async function detectAnomalies(event: SecurityEvent) {
const anomalies = [];
// Unusual login location
const lastLocation = await getLastLoginLocation(event.userId);
if (lastLocation && getDistance(lastLocation, event.location) > 1000) {
anomalies.push({
type: 'unusual_location',
severity: 'medium',
description: 'Login from unusual location',
});
}
// Unusual time
const usualHours = await getUsualActiveHours(event.userId);
if (!isWithinHours(event.timestamp, usualHours)) {
anomalies.push({
type: 'unusual_time',
severity: 'low',
description: 'Activity outside normal hours',
});
}
// Rapid API calls
const recentCalls = await getRecentAPICalls(event.userId, '5m');
if (recentCalls > 100) {
anomalies.push({
type: 'rapid_api_calls',
severity: 'high',
description: 'Abnormally high API usage',
});
}
if (anomalies.length > 0) {
await notifySecurityTeam(event, anomalies);
}
return anomalies;
}8. Enterprise Dashboard
Executive metrics and insights:
typescript
// app/enterprise/dashboard/page.tsx
export default function EnterpriseDashboard() {
const metrics = useEnterpriseMetrics();
return (
<div className="space-y-6">
<div className="grid grid-cols-4 gap-4">
<MetricCard
title="Total Users"
value={metrics.totalUsers}
change={metrics.userGrowth}
icon={<Users />}
/>
<MetricCard
title="Active Teams"
value={metrics.activeTeams}
change={metrics.teamGrowth}
icon={<Building />}
/>
<MetricCard
title="API Usage"
value={`${metrics.apiUsage}M`}
change={metrics.apiGrowth}
icon={<Activity />}
/>
<MetricCard
title="Uptime"
value="99.99%"
change={0.01}
icon={<Shield />}
/>
</div>
<div className="grid grid-cols-2 gap-6">
<ComplianceStatus />
<SecurityOverview />
</div>
<div className="grid grid-cols-3 gap-6">
<UsageByRegion />
<TopAPIEndpoints />
<AuditActivity />
</div>
</div>
);
}Compliance Certifications
Support for various standards:
- SOC 2 Type II: Audit logging, access controls
- GDPR: Data export, deletion, consent management
- HIPAA: Encryption, access logs, BAA support
- ISO 27001: Security controls, risk management
- PCI DSS: Payment data isolation, encryption
License Requirement
Important: This command requires a commercial MakerKit license from https://makerkit.dev?atp=MqaGgc MakerKit is a premium SaaS starter kit and requires proper licensing for commercial use.
