All files / pro/src license.ts

15.06% Statements 11/73
0% Branches 0/30
0% Functions 0/8
16.66% Lines 11/66

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 1502x 2x 2x 2x   2x                                             2x                                                               2x                                                           2x                                                   2x                                       2x                 2x        
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
 
export const LICENSE_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAHPCY4gnBUFQz9PRpWciuKmrZEMeOuIA2DSSoBDxMnbk=
-----END PUBLIC KEY-----`;
 
export interface LicensePayload {
  email: string;
  plan: 'pro' | 'enterprise';
  seats: number;
  issuedAt: string;
  expiresAt: string | null;
}
 
export interface LicenseInfo {
  valid: boolean;
  plan: 'free' | 'pro' | 'enterprise';
  email: string | null;
  seats: number;
  trial: {
    active: boolean;
    daysRemaining: number;
  };
}
 
export function validateLicenseKey(key: string): LicensePayload | null {
  try {
    if (!key.startsWith('CS-PRO-') && !key.startsWith('CS-ENT-')) return null;
 
    const parts = key.split('-');
    const payloadAndSig = parts.slice(2).join('-');
    const [payloadB64, sigB64] = payloadAndSig.split('.');
 
    if (!payloadB64 || !sigB64) return null;
 
    const payloadStr = Buffer.from(payloadB64, 'base64url').toString('utf8');
    const signature = Buffer.from(sigB64, 'base64url');
 
    const publicKey = crypto.createPublicKey(LICENSE_PUBLIC_KEY);
    const isValid = crypto.verify(null, Buffer.from(payloadStr), publicKey, signature);
 
    if (!isValid) return null;
 
    return JSON.parse(payloadStr) as LicensePayload;
  } catch {
    return null;
  }
}
 
function getLicensePath() {
  return path.join(os.homedir(), '.codesession', 'license.json');
}
 
function getInstallPath() {
  return path.join(os.homedir(), '.codesession', 'install.json');
}
 
export function getTrialStatus() {
  const installPath = getInstallPath();
  let installedAt: Date;
 
  if (fs.existsSync(installPath)) {
    try {
      const data = JSON.parse(fs.readFileSync(installPath, 'utf8'));
      installedAt = new Date(data.installedAt);
    } catch {
      installedAt = new Date();
    }
  } else {
    installedAt = new Date();
    try {
      fs.mkdirSync(path.dirname(installPath), { recursive: true });
      fs.writeFileSync(installPath, JSON.stringify({ installedAt: installedAt.toISOString() }, null, 2));
    } catch {}
  }
 
  const now = new Date();
  const diffTime = Math.abs(now.getTime() - installedAt.getTime());
  const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
  const daysRemaining = Math.max(0, 14 - diffDays);
 
  return {
    active: daysRemaining > 0,
    daysRemaining
  };
}
 
export function getLicense(): LicenseInfo {
  const trial = getTrialStatus();
  const defaultInfo: LicenseInfo = { valid: false, plan: 'free', email: null, seats: 1, trial };
 
  const licensePath = getLicensePath();
  if (!fs.existsSync(licensePath)) return defaultInfo;
 
  try {
    const data = JSON.parse(fs.readFileSync(licensePath, 'utf8'));
    if (!data.key) return defaultInfo;
 
    const payload = validateLicenseKey(data.key);
    if (!payload) return defaultInfo;
 
    return {
      valid: true,
      plan: payload.plan,
      email: payload.email,
      seats: payload.seats || 1,
      trial
    };
  } catch {
    return defaultInfo;
  }
}
 
export function activateLicense(key: string) {
  const payload = validateLicenseKey(key);
  if (!payload) return { success: false, error: 'Invalid license key' };
 
  try {
    const licensePath = getLicensePath();
    fs.mkdirSync(path.dirname(licensePath), { recursive: true });
    fs.writeFileSync(licensePath, JSON.stringify({
      key,
      email: payload.email,
      plan: payload.plan,
      activatedAt: new Date().toISOString()
    }, null, 2));
    
    return { success: true, license: payload };
  } catch (err: any) {
    return { success: false, error: err.message || 'Failed to save license' };
  }
}
 
export function deactivateLicense() {
  try {
    const licensePath = getLicensePath();
    if (fs.existsSync(licensePath)) {
      fs.unlinkSync(licensePath);
    }
  } catch {}
}
 
export function isPro(): boolean {
  const info = getLicense();
  return (info.valid && (info.plan === 'pro' || info.plan === 'enterprise')) || info.trial.active;
}