tusktsk
Version:
TuskTsk - The Freedom Configuration Language. Query databases, use any syntax, never bow to any king!
503 lines (439 loc) • 13.6 kB
JavaScript
/**
* License Commands for TuskLang CLI
* =================================
* License validation and management commands
*
* Commands:
* - check: Check current license status
* - activate: Activate license with key
* - validate: Validate license with server
* - info: Display license information
* - transfer: Transfer license to another user
* - revoke: Revoke license
*/
const { Command } = require('commander');
const fs = require('fs').promises;
const path = require('path');
const crypto = require('crypto');
const https = require('https');
const os = require('os');
/**
* License Manager Class
*/
class LicenseManager {
constructor() {
this.licenseFile = path.join(os.homedir(), '.tusktsk', 'license.json');
this.serverUrl = 'https://license.tuskt.sk';
this.license = null;
}
/**
* Initialize license manager
*/
async init() {
try {
// Ensure license directory exists
const licenseDir = path.dirname(this.licenseFile);
await fs.mkdir(licenseDir, { recursive: true });
// Load existing license if available
await this.loadLicense();
} catch (error) {
// License file doesn't exist, that's okay
}
}
/**
* Load license from file
*/
async loadLicense() {
try {
const data = await fs.readFile(this.licenseFile, 'utf8');
this.license = JSON.parse(data);
return this.license;
} catch (error) {
this.license = null;
return null;
}
}
/**
* Save license to file
*/
async saveLicense(licenseData) {
this.license = licenseData;
await fs.writeFile(this.licenseFile, JSON.stringify(licenseData, null, 2), 'utf8');
}
/**
* Generate machine fingerprint
*/
generateMachineFingerprint() {
const platform = os.platform();
const arch = os.arch();
const hostname = os.hostname();
const cpus = os.cpus().length;
const totalMem = os.totalmem();
const fingerprint = crypto.createHash('sha256')
.update(`${platform}-${arch}-${hostname}-${cpus}-${totalMem}`)
.digest('hex');
return fingerprint;
}
/**
* Make HTTPS request to license server
*/
async makeLicenseRequest(endpoint, data = {}) {
return new Promise((resolve, reject) => {
const postData = JSON.stringify({
...data,
machineId: this.generateMachineFingerprint(),
timestamp: Date.now()
});
const options = {
hostname: 'license.tuskt.sk',
port: 443,
path: endpoint,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData),
'User-Agent': 'TuskTsk-CLI/2.0.0'
}
};
const req = https.request(options, (res) => {
let responseData = '';
res.on('data', (chunk) => {
responseData += chunk;
});
res.on('end', () => {
try {
const response = JSON.parse(responseData);
resolve(response);
} catch (error) {
reject(new Error('Invalid server response'));
}
});
});
req.on('error', (error) => {
reject(new Error(`Network error: ${error.message}`));
});
req.write(postData);
req.end();
});
}
/**
* Check license status
*/
async checkStatus() {
await this.init();
if (!this.license) {
return {
valid: false,
status: 'NO_LICENSE',
message: 'No license found. Use "tsk license activate <key>" to activate a license.',
expires: null,
features: []
};
}
const now = Date.now();
const expiresAt = this.license.expiresAt;
if (expiresAt && now > expiresAt) {
return {
valid: false,
status: 'EXPIRED',
message: 'License has expired.',
expires: new Date(expiresAt),
features: this.license.features || []
};
}
return {
valid: true,
status: 'ACTIVE',
message: 'License is active and valid.',
expires: expiresAt ? new Date(expiresAt) : null,
features: this.license.features || [],
license: this.license
};
}
/**
* Activate license
*/
async activate(key) {
if (!key) {
throw new Error('License key is required');
}
try {
const response = await this.makeLicenseRequest('/activate', {
licenseKey: key
});
if (response.success) {
const licenseData = {
key: key,
activatedAt: Date.now(),
expiresAt: response.expiresAt,
features: response.features || [],
user: response.user || 'Unknown',
type: response.type || 'standard',
serverValidated: true
};
await this.saveLicense(licenseData);
return {
success: true,
message: 'License activated successfully',
license: licenseData
};
} else {
throw new Error(response.error || 'License activation failed');
}
} catch (error) {
throw new Error(`Activation failed: ${error.message}`);
}
}
/**
* Validate license with server
*/
async validate(key = null) {
const licenseKey = key || (this.license ? this.license.key : null);
if (!licenseKey) {
throw new Error('No license key provided or found');
}
try {
const response = await this.makeLicenseRequest('/validate', {
licenseKey: licenseKey
});
if (response.success) {
// Update local license with server data
if (this.license) {
this.license.serverValidated = true;
this.license.lastValidated = Date.now();
this.license.features = response.features || this.license.features;
await this.saveLicense(this.license);
}
return {
success: true,
message: 'License validated successfully',
features: response.features || [],
user: response.user || 'Unknown',
expiresAt: response.expiresAt
};
} else {
throw new Error(response.error || 'License validation failed');
}
} catch (error) {
throw new Error(`Validation failed: ${error.message}`);
}
}
/**
* Get license information
*/
async getInfo() {
await this.init();
if (!this.license) {
return {
hasLicense: false,
message: 'No license found'
};
}
const status = await this.checkStatus();
return {
hasLicense: true,
status: status.status,
valid: status.valid,
key: this.license.key ? `${this.license.key.substring(0, 8)}...` : 'Unknown',
activatedAt: this.license.activatedAt ? new Date(this.license.activatedAt) : null,
expiresAt: this.license.expiresAt ? new Date(this.license.expiresAt) : null,
features: this.license.features || [],
user: this.license.user || 'Unknown',
type: this.license.type || 'standard',
serverValidated: this.license.serverValidated || false,
lastValidated: this.license.lastValidated ? new Date(this.license.lastValidated) : null,
machineId: this.generateMachineFingerprint()
};
}
/**
* Transfer license
*/
async transfer(newUser, newEmail) {
if (!this.license) {
throw new Error('No license to transfer');
}
try {
const response = await this.makeLicenseRequest('/transfer', {
licenseKey: this.license.key,
newUser: newUser,
newEmail: newEmail
});
if (response.success) {
// Update local license
this.license.user = newUser;
this.license.transferredAt = Date.now();
await this.saveLicense(this.license);
return {
success: true,
message: 'License transferred successfully',
newUser: newUser
};
} else {
throw new Error(response.error || 'License transfer failed');
}
} catch (error) {
throw new Error(`Transfer failed: ${error.message}`);
}
}
/**
* Revoke license
*/
async revoke() {
if (!this.license) {
throw new Error('No license to revoke');
}
try {
const response = await this.makeLicenseRequest('/revoke', {
licenseKey: this.license.key
});
if (response.success) {
// Remove local license
await fs.unlink(this.licenseFile);
this.license = null;
return {
success: true,
message: 'License revoked successfully'
};
} else {
throw new Error(response.error || 'License revocation failed');
}
} catch (error) {
throw new Error(`Revocation failed: ${error.message}`);
}
}
}
// Global license manager instance
const licenseManager = new LicenseManager();
// Command definitions
const check = new Command('check')
.description('Check current license status')
.option('-v, --verbose', 'Show detailed license information')
.action(async (options) => {
try {
const status = await licenseManager.checkStatus();
if (status.valid) {
console.log(`✅ ${status.message}`);
console.log(` Status: ${status.status}`);
console.log(` Features: ${status.features.length}`);
if (status.expires) {
console.log(` Expires: ${status.expires.toISOString()}`);
}
if (options.verbose) {
const info = await licenseManager.getInfo();
console.log(` User: ${info.user}`);
console.log(` Type: ${info.type}`);
console.log(` Machine ID: ${info.machineId}`);
}
} else {
console.log(`❌ ${status.message}`);
if (status.expires) {
console.log(` Expired: ${status.expires.toISOString()}`);
}
}
} catch (error) {
console.error(`❌ Error checking license: ${error.message}`);
}
});
const activate = new Command('activate')
.description('Activate license with key')
.argument('<key>', 'License key')
.action(async (key) => {
try {
const result = await licenseManager.activate(key);
console.log(`✅ ${result.message}`);
console.log(` License type: ${result.license.type}`);
console.log(` Features: ${result.license.features.length}`);
if (result.license.expiresAt) {
console.log(` Expires: ${new Date(result.license.expiresAt).toISOString()}`);
}
} catch (error) {
console.error(`❌ ${error.message}`);
}
});
const validate = new Command('validate')
.description('Validate license with server')
.argument('[key]', 'License key (uses current license if not provided)')
.action(async (key) => {
try {
const result = await licenseManager.validate(key);
console.log(`✅ ${result.message}`);
console.log(` User: ${result.user}`);
console.log(` Features: ${result.features.length}`);
if (result.expiresAt) {
console.log(` Expires: ${new Date(result.expiresAt).toISOString()}`);
}
} catch (error) {
console.error(`❌ ${error.message}`);
}
});
const info = new Command('info')
.description('Display license information')
.action(async () => {
try {
const info = await licenseManager.getInfo();
if (!info.hasLicense) {
console.log(`❌ ${info.message}`);
return;
}
console.log(`📋 License Information:`);
console.log(` Status: ${info.status}`);
console.log(` Valid: ${info.valid ? 'Yes' : 'No'}`);
console.log(` Key: ${info.key}`);
console.log(` User: ${info.user}`);
console.log(` Type: ${info.type}`);
console.log(` Features: ${info.features.length}`);
console.log(` Machine ID: ${info.machineId}`);
if (info.activatedAt) {
console.log(` Activated: ${info.activatedAt.toISOString()}`);
}
if (info.expiresAt) {
console.log(` Expires: ${info.expiresAt.toISOString()}`);
}
if (info.lastValidated) {
console.log(` Last validated: ${info.lastValidated.toISOString()}`);
}
console.log(` Server validated: ${info.serverValidated ? 'Yes' : 'No'}`);
} catch (error) {
console.error(`❌ Error getting license info: ${error.message}`);
}
});
const transfer = new Command('transfer')
.description('Transfer license to another user')
.argument('<user>', 'New user name')
.argument('<email>', 'New user email')
.action(async (user, email) => {
try {
const result = await licenseManager.transfer(user, email);
console.log(`✅ ${result.message}`);
console.log(` Transferred to: ${result.newUser}`);
} catch (error) {
console.error(`❌ ${error.message}`);
}
});
const revoke = new Command('revoke')
.description('Revoke license')
.option('-f, --force', 'Force revocation without confirmation')
.action(async (options) => {
try {
if (!options.force) {
console.log('⚠️ This will permanently revoke your license. Use --force to confirm.');
return;
}
const result = await licenseManager.revoke();
console.log(`✅ ${result.message}`);
} catch (error) {
console.error(`❌ ${error.message}`);
}
});
module.exports = {
check,
activate,
validate,
info,
transfer,
revoke,
// Export for testing
_LicenseManager: LicenseManager,
_licenseManager: licenseManager
};