UNPKG

@neurolint/cli

Version:

NeuroLint CLI for React/Next.js modernization with advanced 6-layer orchestration and intelligent AST transformations

260 lines (221 loc) 6.5 kB
const { createClient } = require('@supabase/supabase-js'); const fs = require('fs-extra'); const path = require('path'); const os = require('os'); require('dotenv').config(); // Use production Supabase URL from the main project const supabase = createClient( process.env.SUPABASE_URL || 'https://app.neurolint.dev', process.env.SUPABASE_ANON_KEY || 'your-anon-key' ); class AuthManager { constructor() { this.user = null; this.configPath = path.join(os.homedir(), '.neurolint', 'config.json'); this.loadStoredAuth(); } async loadStoredAuth() { try { if (await fs.pathExists(this.configPath)) { const config = await fs.readJson(this.configPath); if (config.session) { const { data, error } = await supabase.auth.setSession(config.session); if (!error && data.user) { this.user = data.user; } } } } catch (error) { // Ignore auth loading errors - user will need to login } } async saveSession(session) { try { await fs.ensureDir(path.dirname(this.configPath)); await fs.writeJson(this.configPath, { session }, { spaces: 2 }); } catch (error) { console.warn('Failed to save session:', error.message); } } async login(email, password) { try { const { data, error } = await supabase.auth.signInWithPassword({ email, password }); if (error) throw error; this.user = data.user; // Save session for persistent login if (data.session) { await this.saveSession(data.session); } // Get or create user profile await this.ensureUserProfile(); return { success: true, user: data.user }; } catch (error) { return { success: false, error: error.message }; } } async signup(email, password) { try { const { data, error } = await supabase.auth.signUp({ email, password }); if (error) throw error; return { success: true, user: data.user, needsConfirmation: !data.session }; } catch (error) { return { success: false, error: error.message }; } } async logout() { try { await supabase.auth.signOut(); this.user = null; // Clear stored session if (await fs.pathExists(this.configPath)) { await fs.remove(this.configPath); } return { success: true }; } catch (error) { return { success: false, error: error.message }; } } async ensureUserProfile() { if (!this.user) return; try { // Check if profile exists const { data: existingProfile } = await supabase .from('user_profiles') .select('*') .eq('id', this.user.id) .single(); if (!existingProfile) { // Create profile with free tier defaults const { error } = await supabase .from('user_profiles') .insert({ id: this.user.id, email: this.user.email, tier: 'free', usage: { remainingFixes: 5, usedFixes: 0, monthlyResetDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString() } }); if (error) { console.warn('Failed to create user profile:', error.message); } } } catch (error) { console.warn('Failed to ensure user profile:', error.message); } } isAuthenticated() { return !!this.user; } getUserId() { return this.user?.id; } getUserEmail() { return this.user?.email; } async checkUsageLimit(action) { if (!this.user) { return { canUse: false, reason: 'Authentication required' }; } try { const { data: profile, error } = await supabase .from('user_profiles') .select('tier, usage') .eq('id', this.user.id) .single(); if (error) throw error; const tier = profile.tier || 'free'; const usage = profile.usage || { remainingFixes: -1 }; // Tier limits - aligned with pricing structure const limits = { free: { analyze: -1, // unlimited analyze fix: -1, // unlimited fixes for layers 1-2 only layers: [1, 2] // restricted to layers 1-2 }, basic: { analyze: -1, fix: -1, // unlimited fixes for layers 1-4 layers: [1, 2, 3, 4] }, professional: { analyze: -1, fix: -1, // unlimited all layers layers: [1, 2, 3, 4, 5, 6] }, business: { analyze: -1, fix: -1, layers: [1, 2, 3, 4, 5, 6] }, enterprise: { analyze: -1, fix: -1, layers: [1, 2, 3, 4, 5, 6] }, premium: { analyze: -1, fix: -1, layers: [1, 2, 3, 4, 5, 6] } }; const tierLimits = limits[tier] || limits.free; const actionLimit = tierLimits[action]; if (actionLimit === -1) { return { canUse: true, allowedLayers: tierLimits.layers }; // unlimited within allowed layers } return { canUse: false, reason: `${tier} tier limit reached. Upgrade at https://neurolint.dev/pricing` }; } catch (error) { console.error('Usage check failed:', error); return { canUse: false, reason: 'Unable to verify usage limits' }; } } async recordUsage(action, metadata = {}) { if (!this.user) return; try { // Record usage log const { error: logError } = await supabase .from('usage_logs') .insert({ user_id: this.user.id, action, metadata, timestamp: new Date().toISOString() }); if (logError) { console.warn('Failed to log usage:', logError.message); } // Decrement remaining fixes for fix actions if (action === 'fix') { const { error: updateError } = await supabase.rpc('decrement_user_fixes', { p_user_id: this.user.id, p_count: metadata.filesFixed || 1 }); if (updateError) { console.warn('Failed to update usage limits:', updateError.message); } } } catch (error) { console.warn('Failed to record usage:', error.message); } } } module.exports = { AuthManager, supabase };