Files
motovaultpro/backend/src/core/config/feature-tiers.ts
Eric Gullickson 5cbf9c764d feat: rewire vehicles controller to OCR VIN decode (refs #226)
Replace NHTSAClient with OcrClient in vehicles controller. Move cache
logic into VehiclesService with format-aware reads (Gemini vs legacy
NHTSA entries). Rename nhtsaValue to sourceValue in MatchedField.
Remove vpic config from Zod schema and YAML config files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:47:47 -06:00

161 lines
4.7 KiB
TypeScript

/**
* @ai-summary Feature tier configuration and utilities
* @ai-context Defines feature-to-tier mapping for gating premium features
*/
import { SubscriptionTier } from '../../features/user-profile/domain/user-profile.types';
// Tier hierarchy: higher number = higher access level
export const TIER_LEVELS: Record<SubscriptionTier, number> = {
free: 0,
pro: 1,
enterprise: 2,
} as const;
// Feature configuration interface
export interface FeatureConfig {
minTier: SubscriptionTier;
name: string;
upgradePrompt: string;
}
// Feature registry - add new gated features here
export const FEATURE_TIERS: Record<string, FeatureConfig> = {
'document.scanMaintenanceSchedule': {
minTier: 'pro',
name: 'Scan for Maintenance Schedule',
upgradePrompt: 'Upgrade to Pro to automatically extract maintenance schedules from your vehicle manuals.',
},
'vehicle.vinDecode': {
minTier: 'pro',
name: 'VIN Decode',
upgradePrompt: 'Upgrade to Pro to automatically decode VIN and populate vehicle details from the vehicle database.',
},
'fuelLog.receiptScan': {
minTier: 'pro',
name: 'Receipt Scan',
upgradePrompt: 'Upgrade to Pro to scan fuel receipts and auto-fill your fuel log entries.',
},
'maintenance.receiptScan': {
minTier: 'pro',
name: 'Maintenance Receipt Scan',
upgradePrompt: 'Upgrade to Pro to scan maintenance receipts and extract service details automatically.',
},
} as const;
/**
* Get numeric level for a subscription tier
*/
export function getTierLevel(tier: SubscriptionTier): number {
return TIER_LEVELS[tier] ?? 0;
}
/**
* Check if a user tier can access a feature
* Higher tiers inherit access to all lower tier features
*/
export function canAccessFeature(userTier: SubscriptionTier, featureKey: string): boolean {
const feature = FEATURE_TIERS[featureKey];
if (!feature) {
// Unknown features are accessible by all (fail open for unlisted features)
return true;
}
return getTierLevel(userTier) >= getTierLevel(feature.minTier);
}
/**
* Get the minimum required tier for a feature
* Returns null if feature is not gated
*/
export function getRequiredTier(featureKey: string): SubscriptionTier | null {
const feature = FEATURE_TIERS[featureKey];
return feature?.minTier ?? null;
}
/**
* Get full feature configuration
* Returns undefined if feature is not registered
*/
export function getFeatureConfig(featureKey: string): FeatureConfig | undefined {
return FEATURE_TIERS[featureKey];
}
/**
* Get all feature configurations (for API endpoint)
*/
export function getAllFeatureConfigs(): Record<string, FeatureConfig> {
return { ...FEATURE_TIERS };
}
// Vehicle limits per tier
// null indicates unlimited (enterprise tier)
export const VEHICLE_LIMITS: Record<SubscriptionTier, number | null> = {
free: 2,
pro: 5,
enterprise: null,
} as const;
/**
* Vehicle limits vary by subscription tier and must be queryable
* at runtime for both backend enforcement and frontend UI state.
*
* @param tier - User's subscription tier
* @returns Maximum vehicles allowed, or null for unlimited (enterprise tier)
*/
export function getVehicleLimit(tier: SubscriptionTier): number | null {
return VEHICLE_LIMITS[tier] ?? null;
}
/**
* Check if a user can add another vehicle based on their tier and current count.
*
* @param tier - User's subscription tier
* @param currentCount - Number of vehicles user currently has
* @returns true if user can add another vehicle, false if at/over limit
*/
export function canAddVehicle(tier: SubscriptionTier, currentCount: number): boolean {
const limit = getVehicleLimit(tier);
// null limit means unlimited (enterprise)
if (limit === null) {
return true;
}
return currentCount < limit;
}
/**
* Vehicle limit configuration with upgrade prompt.
* Structure supports additional resource types in the future.
*/
export interface VehicleLimitConfig {
limit: number | null;
tier: SubscriptionTier;
upgradePrompt: string;
}
/**
* Get vehicle limit configuration with upgrade prompt for a tier.
*
* @param tier - User's subscription tier
* @returns Configuration with limit and upgrade prompt
*/
export function getVehicleLimitConfig(tier: SubscriptionTier): VehicleLimitConfig {
const limit = getVehicleLimit(tier);
const defaultPrompt = 'Upgrade to access additional vehicles.';
let upgradePrompt: string;
if (tier === 'free') {
upgradePrompt = 'Free tier is limited to 2 vehicles. Upgrade to Pro for up to 5 vehicles, or Enterprise for unlimited.';
} else if (tier === 'pro') {
upgradePrompt = 'Pro tier is limited to 5 vehicles. Upgrade to Enterprise for unlimited vehicles.';
} else {
upgradePrompt = defaultPrompt;
}
return {
limit,
tier,
upgradePrompt,
};
}