- Build complete Next.js CRM for commercial real estate - Add authentication with JWT sessions and role-based access - Add GoHighLevel API integration for contacts, conversations, opportunities - Add AI-powered Control Center with tool calling - Add Setup page with onboarding checklist (/setup) - Add sidebar navigation with Setup menu item - Fix type errors in onboarding API, GHL services, and control center tools - Add Prisma schema with SQLite for local development - Add UI components with clay morphism design system Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
import crypto from 'crypto';
|
|
|
|
const ALGORITHM = 'aes-256-gcm';
|
|
const IV_LENGTH = 16;
|
|
const AUTH_TAG_LENGTH = 16;
|
|
|
|
// Use environment variable or generate a key
|
|
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY ||
|
|
process.env.SETTINGS_ENCRYPTION_KEY ||
|
|
'0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
|
|
|
|
export function encrypt(text: string): string {
|
|
const iv = crypto.randomBytes(IV_LENGTH);
|
|
const cipher = crypto.createCipheriv(
|
|
ALGORITHM,
|
|
Buffer.from(ENCRYPTION_KEY, 'hex'),
|
|
iv
|
|
);
|
|
|
|
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
encrypted += cipher.final('hex');
|
|
|
|
const authTag = cipher.getAuthTag();
|
|
|
|
// Combine IV + authTag + encrypted data
|
|
return iv.toString('hex') + authTag.toString('hex') + encrypted;
|
|
}
|
|
|
|
export function decrypt(encryptedText: string): string {
|
|
const iv = Buffer.from(encryptedText.slice(0, IV_LENGTH * 2), 'hex');
|
|
const authTag = Buffer.from(
|
|
encryptedText.slice(IV_LENGTH * 2, IV_LENGTH * 2 + AUTH_TAG_LENGTH * 2),
|
|
'hex'
|
|
);
|
|
const encrypted = encryptedText.slice(IV_LENGTH * 2 + AUTH_TAG_LENGTH * 2);
|
|
|
|
const decipher = crypto.createDecipheriv(
|
|
ALGORITHM,
|
|
Buffer.from(ENCRYPTION_KEY, 'hex'),
|
|
iv
|
|
);
|
|
decipher.setAuthTag(authTag);
|
|
|
|
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
decrypted += decipher.final('utf8');
|
|
|
|
return decrypted;
|
|
}
|
|
|
|
// Mask sensitive values for display
|
|
export function maskValue(value: string, showLast = 4): string {
|
|
if (value.length <= showLast) return '****';
|
|
return '*'.repeat(value.length - showLast) + value.slice(-showLast);
|
|
}
|