@neurolint/cli
Version:
NeuroLint CLI for React/Next.js modernization with advanced 6-layer orchestration and intelligent AST transformations
243 lines (201 loc) • 6.2 kB
JavaScript
const { createClient } = require('@supabase/supabase-js');
const fs = require('fs-extra');
const path = require('path');
const os = require('os');
require('dotenv').config();
// Validate required environment variables
const SUPABASE_URL = process.env.SUPABASE_URL;
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY;
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
console.error('Missing required environment variables: SUPABASE_URL and SUPABASE_ANON_KEY');
process.exit(1);
}
// Use production Supabase configuration
const supabase = createClient(SUPABASE_URL, SUPABASE_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: 5 };
// Tier limits from your plan
const limits = {
free: { analyze: -1, fix: 5 }, // unlimited analyze, 5 fixes
basic: { analyze: -1, fix: 2000 },
professional: { analyze: -1, fix: -1 },
business: { analyze: -1, fix: -1 },
enterprise: { analyze: -1, fix: -1 },
premium: { analyze: -1, fix: -1 }
};
const tierLimits = limits[tier] || limits.free;
const actionLimit = tierLimits[action];
if (actionLimit === -1) {
return { canUse: true }; // unlimited
}
if (usage.remainingFixes > 0) {
return { canUse: true, remaining: usage.remainingFixes };
}
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 };