UNPKG

@synvo_ai/mcp-server

Version:

Enterprise-grade MCP server for Synvo AI - File management and AI query capabilities for Claude Desktop, VSCode, and Cursor

328 lines (327 loc) • 10.2 kB
/** * License Management Module for Synvo MCP Server * Handles license verification, quota management, and user authentication */ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { homedir } from 'os'; import { join } from 'path'; import axios from 'axios'; import { createHash } from 'crypto'; // Configuration const LICENSE_SERVER = process.env.LICENSE_SERVER || 'https://license.synvo.ai/v1'; const LICENSE_DIR = join(homedir(), '.synvo'); const LICENSE_FILE = join(LICENSE_DIR, 'license.json'); const CACHE_DURATION_HOURS = 24; // Default quotas for each tier const DEFAULT_QUOTAS = { free: { uploads_per_day: 10, queries_per_day: 50, max_file_size_mb: 5, }, pro: { uploads_per_day: 1000, queries_per_day: 10000, max_file_size_mb: 100, }, team: { uploads_per_day: 10000, queries_per_day: 100000, max_file_size_mb: 500, }, enterprise: { uploads_per_day: 999999, queries_per_day: 999999, max_file_size_mb: 5000, }, }; /** * Generate a unique machine identifier * This is used to bind licenses to specific machines (optional) */ export function getMachineId() { try { const os = require('os'); const platform = process.platform; const arch = process.arch; const hostname = os.hostname(); const cpus = os.cpus(); // Create a fingerprint from system info const fingerprint = [ platform, arch, hostname, cpus.length, cpus[0]?.model || 'unknown', ].join('-'); return createHash('sha256').update(fingerprint).digest('hex').substring(0, 32); } catch (error) { console.error('[License] Failed to generate machine ID:', error); return 'unknown'; } } /** * Load license from local file */ export function loadLicense() { try { if (!existsSync(LICENSE_FILE)) { return null; } const data = readFileSync(LICENSE_FILE, 'utf-8'); const license = JSON.parse(data); // Validate license structure if (!license.license_key || !license.tier || !license.cached_quota) { console.error('[License] Invalid license file format'); return null; } return license; } catch (error) { console.error('[License] Failed to load license:', error); return null; } } /** * Save license to local file */ export function saveLicense(license) { try { // Ensure directory exists if (!existsSync(LICENSE_DIR)) { mkdirSync(LICENSE_DIR, { recursive: true }); } // Write license file writeFileSync(LICENSE_FILE, JSON.stringify(license, null, 2), 'utf-8'); console.error('[License] License saved successfully'); } catch (error) { console.error('[License] Failed to save license:', error); throw new Error('Failed to save license file'); } } /** * Delete local license file */ export function deleteLicense() { try { if (existsSync(LICENSE_FILE)) { require('fs').unlinkSync(LICENSE_FILE); console.error('[License] License deleted'); } } catch (error) { console.error('[License] Failed to delete license:', error); } } /** * Verify license with remote server */ export async function verifyLicense(licenseKey) { try { console.error('[License] Verifying license with server...'); const response = await axios.post(`${LICENSE_SERVER}/verify`, { license_key: licenseKey, machine_id: getMachineId(), version: getPackageVersion(), }, { timeout: 10000, headers: { 'Content-Type': 'application/json', }, }); console.error(`[License] Verification response: valid=${response.data.valid}, tier=${response.data.tier}`); return response.data; } catch (error) { if (axios.isAxiosError(error)) { console.error('[License] Verification failed:', error.response?.data || error.message); // Return error response return { valid: false, tier: 'free', expires_at: '', quota: DEFAULT_QUOTAS.free, error: error.response?.data?.error || 'License verification failed', }; } console.error('[License] Unknown verification error:', error); throw new Error('License verification failed'); } } /** * Check if license needs revalidation */ export function needsRevalidation(license) { try { const lastVerified = new Date(license.last_verified); const now = new Date(); const hoursSinceVerification = (now.getTime() - lastVerified.getTime()) / 1000 / 60 / 60; return hoursSinceVerification > CACHE_DURATION_HOURS; } catch (error) { console.error('[License] Failed to check revalidation:', error); return true; // Force revalidation on error } } /** * Check if license is expired */ export function isLicenseExpired(license) { if (!license.expires_at) { return false; // No expiration } try { const expiresAt = new Date(license.expires_at); const now = new Date(); return now > expiresAt; } catch (error) { console.error('[License] Failed to check expiration:', error); return false; } } /** * Get current license status (with caching and fallback) */ export async function getLicenseStatus() { const license = loadLicense(); // No license → Free tier if (!license) { console.error('[License] No license found, using Free tier'); return { tier: 'free', quota: DEFAULT_QUOTAS.free, is_valid: true, }; } // Check expiration if (isLicenseExpired(license)) { console.error('[License] License expired, reverting to Free tier'); return { tier: 'free', quota: DEFAULT_QUOTAS.free, is_valid: false, }; } // Check if needs revalidation if (needsRevalidation(license)) { console.error('[License] License needs revalidation, checking server...'); try { const verification = await verifyLicense(license.license_key); if (!verification.valid) { console.error('[License] License invalid, reverting to Free tier'); return { tier: 'free', quota: DEFAULT_QUOTAS.free, is_valid: false, }; } // Update cached license saveLicense({ license_key: license.license_key, tier: verification.tier, last_verified: new Date().toISOString(), cached_quota: verification.quota, expires_at: verification.expires_at, }); return { tier: verification.tier, quota: verification.quota, usage: verification.usage, is_valid: true, expires_at: verification.expires_at, }; } catch (error) { // Revalidation failed, use cached data console.warn('[License] Revalidation failed, using cached license data'); return { tier: license.tier, quota: license.cached_quota, is_valid: true, // Trust cached data for now }; } } // Use cached license data return { tier: license.tier, quota: license.cached_quota, is_valid: true, expires_at: license.expires_at, }; } /** * Activate a license (CLI command) */ export async function activateLicense(licenseKey) { console.log('šŸ” Verifying license key...'); // Verify with server const verification = await verifyLicense(licenseKey); if (!verification.valid) { throw new Error(verification.error || 'Invalid license key'); } // Save license saveLicense({ license_key: licenseKey, tier: verification.tier, last_verified: new Date().toISOString(), cached_quota: verification.quota, expires_at: verification.expires_at, }); console.log(`\nāœ… License activated successfully!`); console.log(` Tier: ${verification.tier.toUpperCase()}`); console.log(` Expires: ${verification.expires_at || 'Never'}`); console.log(`\nšŸ“Š Your Quotas:`); console.log(` Daily Uploads: ${verification.quota.uploads_per_day}`); console.log(` Daily Queries: ${verification.quota.queries_per_day}`); console.log(` Max File Size: ${verification.quota.max_file_size_mb}MB`); } /** * Deactivate license (CLI command) */ export function deactivateLicense() { const license = loadLicense(); if (!license) { console.log('ā„¹ļø No license is currently activated.'); return; } deleteLicense(); console.log('āœ… License deactivated successfully.'); console.log(' You are now using the Free tier.'); } /** * Get package version */ function getPackageVersion() { try { const packageJson = require('../package.json'); return packageJson.version || '1.0.0'; } catch (error) { return '1.0.0'; } } /** * Format quota for display */ export function formatQuota(quota) { return ` Daily Uploads: ${quota.uploads_per_day === 999999 ? 'Unlimited' : quota.uploads_per_day} Daily Queries: ${quota.queries_per_day === 999999 ? 'Unlimited' : quota.queries_per_day} Max File Size: ${quota.max_file_size_mb}MB`; } /** * Get upgrade message for free tier users */ export function getUpgradeMessage() { return ` šŸ’” Upgrade to Pro for higher limits! • 1,000 daily uploads • 10,000 daily queries • 100MB max file size • Priority support Visit: https://synvo.ai/pricing`; } //# sourceMappingURL=license.js.map