UNPKG

@moontra/moonui-cli

Version:

CLI tool for MoonUI component library

775 lines (769 loc) 24.5 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw new Error('Dynamic require of "' + x + '" is not supported'); }); var __commonJS = (cb, mod) => function __require2() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // package.json var require_package = __commonJS({ "package.json"(exports, module) { module.exports = { name: "@moontra/moonui-cli", version: "2.13.5", description: "CLI tool for MoonUI component library", main: "dist/index.js", types: "dist/index.d.ts", bin: { moonui: "./dist/index.js" }, files: [ "dist", "src/postcss", "src/vite", "src/theme-presets.ts", "src/utils/theme-generator.ts" ], exports: { ".": { types: "./dist/index.d.ts", import: "./dist/index.js", require: "./dist/index.cjs" }, "./postcss/moonui-theme-plugin": { types: "./src/postcss/moonui-theme-plugin.d.ts", require: "./src/postcss/moonui-theme-plugin.js" }, "./vite/moonui-theme-plugin": { types: "./dist/vite/moonui-theme-plugin.d.ts", import: "./dist/vite/moonui-theme-plugin.js" } }, scripts: { build: "tsup src/index.ts --format cjs,esm --dts", dev: "tsup src/index.ts --format cjs,esm --dts --watch --sourcemap", clean: "rm -rf dist", lint: 'eslint "src/**/*.ts*"', test: "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage", prepublishOnly: "npm run clean && npm run build", pub: "npm version patch && npm run build && npm publish --access=public", "pub:minor": "npm version minor && npm run build && npm publish --access=public", "pub:major": "npm version major && npm run build && npm publish --access=public" }, keywords: [ "ui", "components", "cli", "moonui" ], author: "MoonUI", license: "MIT", dependencies: { "@radix-ui/react-primitive": "^2.1.3", "@types/jsonwebtoken": "^9.0.10", "@types/node-fetch": "^2.6.12", "@types/react-syntax-highlighter": "^15.5.13", "adm-zip": "^0.5.10", axios: "^1.6.2", chalk: "^4.1.2", "class-variance-authority": "^0.7.1", clsx: "^2.1.1", commander: "^10.0.1", "cross-spawn": "^7.0.3", degit: "^2.8.4", figlet: "^1.6.0", "fs-extra": "^11.1.1", inquirer: "^9.2.14", jsonwebtoken: "^9.0.2", "node-fetch": "^3.3.2", open: "^8.4.2", ora: "^5.4.1", prompts: "^2.4.2", "react-syntax-highlighter": "^15.6.1", "tailwind-merge": "^3.3.1", "ts-morph": "^18.0.0", "validate-npm-package-name": "^5.0.0", zod: "^3.22.4" }, devDependencies: { "@next/bundle-analyzer": "^15.4.6", "@sentry/nextjs": "^10.3.0", "@svgr/webpack": "^8.1.0", "@types/adm-zip": "^0.5.5", "@types/cross-spawn": "^6.0.6", "@types/degit": "^2.8.6", "@types/figlet": "^1.7.0", "@types/fs-extra": "^11.0.4", "@types/inquirer": "^9.0.9", "@types/node": "^18.16.0", "@types/ora": "^3.1.0", "@types/prompts": "^2.4.9", "@types/validate-npm-package-name": "^4.0.2", autoprefixer: "^10.4.21", eslint: "^8.39.0", jest: "^29.5.0", postcss: "^8.5.6", tailwindcss: "^4.1.11", tsup: "^6.7.0", typescript: "^5.0.4" } }; } }); // src/services/auth-service.ts import fs from "fs-extra"; import path from "path"; import os2 from "os"; import crypto2 from "crypto"; import chalk from "chalk"; // src/services/device.service.ts import os from "os"; import crypto from "crypto"; import { networkInterfaces } from "os"; var DeviceService = class { /** * Generate a unique device fingerprint based on system attributes */ static getDeviceFingerprint() { const deviceInfo = this.getDeviceInfo(); const fingerprintData = [ deviceInfo.hostname, deviceInfo.username, deviceInfo.platform, deviceInfo.arch, deviceInfo.cpuModel, deviceInfo.cpuCount.toString(), deviceInfo.macAddress || "no-mac" ].join("|"); return crypto.createHash("sha256").update(fingerprintData).digest("hex"); } /** * Get detailed device information */ static getDeviceInfo() { const cpus = os.cpus(); const macAddress = this.getMacAddress(); const packageJson = require_package(); const cliVersion = packageJson.version || "unknown"; return { hostname: os.hostname(), username: os.userInfo().username, platform: os.platform(), arch: os.arch(), cpuModel: cpus[0]?.model || "unknown", cpuCount: cpus.length, totalMemory: os.totalmem(), nodeVersion: process.version, cliVersion, macAddress }; } /** * Get the primary MAC address of the device */ static getMacAddress() { const interfaces = networkInterfaces(); for (const name of Object.keys(interfaces)) { const iface = interfaces[name]; if (!iface) continue; for (const details of iface) { if (!details.internal && details.mac && details.mac !== "00:00:00:00:00:00") { return details.mac; } } } for (const name of Object.keys(interfaces)) { const iface = interfaces[name]; if (!iface) continue; for (const details of iface) { if (details.mac && details.mac !== "00:00:00:00:00:00") { return details.mac; } } } return void 0; } /** * Validate device against API */ static async validateDevice(apiKey, deviceId) { try { const API_BASE2 = "https://moonui.dev"; const response = await fetch(`${API_BASE2}/api/device/validate`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}`, "X-Device-ID": deviceId }, body: JSON.stringify({ deviceId, deviceInfo: this.getDeviceInfo() }) }); return response.ok; } catch (error) { console.error("Device validation error:", error); return false; } } /** * Register a new device */ static async registerDevice(apiKey) { try { const deviceId = this.getDeviceFingerprint(); const deviceInfo = this.getDeviceInfo(); const API_BASE2 = "https://moonui.dev"; const response = await fetch(`${API_BASE2}/api/device/register`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` }, body: JSON.stringify({ deviceId, deviceInfo }) }); if (response.ok) { return { success: true, deviceId }; } else { const error = await response.json(); return { success: false, error: error.message || "Failed to register device" }; } } catch (error) { return { success: false, error: error.message || "Device registration failed" }; } } /** * List all registered devices for the user */ static async listDevices(apiKey) { try { const API_BASE2 = "https://moonui.dev"; const response = await fetch(`${API_BASE2}/api/device/list`, { headers: { "Authorization": `Bearer ${apiKey}` } }); if (response.ok) { const data = await response.json(); return data.devices || []; } return []; } catch (error) { console.error("Failed to list devices:", error); return []; } } /** * Revoke a device */ static async revokeDevice(apiKey, deviceId) { try { const API_BASE2 = "https://moonui.dev"; const response = await fetch(`${API_BASE2}/api/device/revoke`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` }, body: JSON.stringify({ deviceId }) }); return response.ok; } catch (error) { console.error("Failed to revoke device:", error); return false; } } /** * Get current device status */ static async getDeviceStatus(apiKey) { try { const deviceId = this.getDeviceFingerprint(); const API_BASE2 = "https://moonui.dev"; const response = await fetch(`${API_BASE2}/api/device/status`, { headers: { "Authorization": `Bearer ${apiKey}`, "X-Device-ID": deviceId } }); if (response.ok) { return await response.json(); } return null; } catch (error) { console.error("Failed to get device status:", error); return null; } } }; // src/services/auth-service.ts var API_BASE = "https://moonui.dev"; var CACHE_DURATION = 24 * 60 * 60 * 1e3; var REFRESH_THRESHOLD = 60 * 60 * 1e3; var MOONUI_SECRET_KEY = "moonui-2024-auth-encryption-key-v1"; var AuthService = class { constructor() { const configDir = path.join(os2.homedir(), ".moonui"); this.authPath = path.join(configDir, "auth.encrypted"); this.encryptionKey = this.getEncryptionKey(); } static getInstance() { if (!this.instance) { this.instance = new AuthService(); } return this.instance; } getDeviceFingerprint() { const platform = os2.platform(); const hostname = os2.hostname(); const username = os2.userInfo().username; return `${platform}-${hostname}-${username}`; } getEncryptionKey() { const devicePart = this.getDeviceFingerprint(); return crypto2.pbkdf2Sync( MOONUI_SECRET_KEY + devicePart, "moonui-salt-2025", 1e4, 32, "sha256" ); } encrypt(data) { const iv = crypto2.randomBytes(16); const cipher = crypto2.createCipheriv("aes-256-cbc", this.encryptionKey, iv); let encrypted = cipher.update(data, "utf8", "hex"); encrypted += cipher.final("hex"); return iv.toString("hex") + ":" + encrypted; } decrypt(data) { const parts = data.split(":"); const iv = Buffer.from(parts[0], "hex"); const encrypted = parts[1]; const decipher = crypto2.createDecipheriv("aes-256-cbc", this.encryptionKey, iv); let decrypted = decipher.update(encrypted, "hex", "utf8"); decrypted += decipher.final("utf8"); return decrypted; } generateSignature(token) { const data = JSON.stringify({ accessToken: token.accessToken, userId: token.user.id, plan: token.user.plan, expiresAt: token.expiresAt }); return crypto2.createHmac("sha256", this.encryptionKey).update(data).digest("hex"); } verifySignature(token) { if (!token.signature) return false; const expectedSignature = this.generateSignature(token); return token.signature === expectedSignature; } async saveAuth(token) { try { token.signature = this.generateSignature(token); token.lastVerified = (/* @__PURE__ */ new Date()).toISOString(); const deviceFingerprint = this.getDeviceFingerprint(); token.deviceId = deviceFingerprint; const cache = { token, deviceId: deviceFingerprint, machineId: deviceFingerprint, encryptedAt: (/* @__PURE__ */ new Date()).toISOString(), // Extended user info for local auth userInfo: { email: token.user.email, plan: token.user.plan, hasProAccess: token.user.hasProAccess || token.user.plan !== "free", expiresAt: token.expiresAt, features: token.user.features || [], hasLifetimeAccess: token.user.hasLifetimeAccess } }; const encrypted = this.encrypt(JSON.stringify(cache)); const hmac = crypto2.createHmac("sha256", this.encryptionKey); const [iv, encryptedData] = encrypted.split(":"); hmac.update(encryptedData); const signature = hmac.digest("hex"); const finalData = `${iv}.${encryptedData}.${signature}`; const configDir = path.dirname(this.authPath); await fs.ensureDir(configDir); await fs.writeFile(this.authPath, finalData, "utf8"); await fs.chmod(this.authPath, 384); } catch (error) { throw new Error(`Failed to save auth: ${error.message}`); } } async clearAuth() { try { const configDir = path.join(os2.homedir(), ".moonui"); const filesToRemove = [ this.authPath, path.join(configDir, "auth.json"), path.join(configDir, "auth.encrypted"), path.join(configDir, "device.json"), path.join(configDir, "cache"), path.join(configDir, ".session"), path.join(configDir, ".token"), path.join(configDir, ".refresh"), path.join(configDir, ".device"), path.join(configDir, ".fingerprint") ]; for (const file of filesToRemove) { try { if (await fs.pathExists(file)) { await fs.remove(file); } } catch { } } const cacheDir = path.join(configDir, "cache"); try { if (await fs.pathExists(cacheDir)) { await fs.emptyDir(cacheDir); } } catch { } } catch (error) { throw new Error(`Failed to clear auth: ${error.message}`); } } async getAuth() { try { if (!await fs.pathExists(this.authPath)) { return null; } const fileContent = await fs.readFile(this.authPath, "utf8"); const parts = fileContent.split("."); if (parts.length !== 3) { return null; } const [iv, encryptedData, signature] = parts; const hmac = crypto2.createHmac("sha256", this.encryptionKey); hmac.update(encryptedData); const computedSignature = hmac.digest("hex"); if (computedSignature !== signature) { return null; } const encrypted = `${iv}:${encryptedData}`; const decrypted = this.decrypt(encrypted); const cache = JSON.parse(decrypted); const currentDeviceId = this.getDeviceFingerprint(); if (cache.deviceId && cache.deviceId !== currentDeviceId) { console.warn(chalk.red("\u26A0\uFE0F This license is registered to a different device")); console.warn(chalk.yellow(" MoonUI Pro licenses are device-specific")); console.warn(chalk.yellow(" Please login again or contact support for multi-device access")); await this.clearAuth(); return null; } if (!cache.token.deviceId) { cache.token.deviceId = currentDeviceId; } if (!this.verifySignature(cache.token)) { console.warn(chalk.red("Auth token signature verification failed")); await this.clearAuth(); return null; } const expiresAt = new Date(cache.token.expiresAt); const now = /* @__PURE__ */ new Date(); if (expiresAt < now) { if (cache.token.refreshToken) { try { const newToken = await this.refreshToken(cache.token.refreshToken); await this.saveAuth(newToken); return newToken; } catch (error) { return null; } } return null; } const lastVerified = new Date(cache.token.lastVerified); if (now.getTime() - lastVerified.getTime() > REFRESH_THRESHOLD) { this.backgroundRefresh(cache.token); } return cache.token; } catch (error) { await this.clearAuth(); return null; } } async backgroundRefresh(token) { try { const response = await fetch(`${API_BASE}/api/auth/verify`, { method: "POST", headers: { "Authorization": `Bearer ${token.accessToken}`, "Content-Type": "application/json" } }); if (response.ok) { const data = await response.json(); token.user = data.user; token.lastVerified = (/* @__PURE__ */ new Date()).toISOString(); token.signature = this.generateSignature(token); await this.saveAuth(token); } } catch { } } async refreshToken(refreshToken) { const response = await fetch(`${API_BASE}/api/auth/refresh`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ refresh_token: refreshToken, grant_type: "refresh_token" }) }); if (!response.ok) { throw new Error("Token refresh failed"); } const data = await response.json(); return { accessToken: data.access_token, refreshToken: data.refresh_token, expiresAt: data.expires_at, user: data.user, lastVerified: (/* @__PURE__ */ new Date()).toISOString() }; } async isAuthenticated(skipServerValidation = false) { const auth = await this.getAuth(); if (!auth) return false; if (skipServerValidation) { return true; } const isValid = await this.validateTokenWithServer(auth.accessToken); if (!isValid) { await this.clearAuth(); return false; } return true; } async requireAuth() { const auth = await this.getAuth(); if (!auth) { console.log(chalk.yellow("\n\u{1F510} Authentication required")); console.log(chalk.gray("Please login to continue:")); console.log(chalk.cyan(" moonui login\n")); process.exit(1); } const isValid = await this.validateTokenWithServer(auth.accessToken); if (!isValid) { console.log(chalk.red("\n\u274C Your session has been revoked")); console.log(chalk.yellow("Please login again:")); console.log(chalk.cyan(" moonui login\n")); await this.clearAuth(); process.exit(1); } return auth; } async validateTokenWithServer(token) { try { const deviceId = this.getDeviceFingerprint(); const response = await fetch(`${API_BASE}/api/device/validate`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}`, "X-Device-ID": deviceId }, body: JSON.stringify({ deviceId }) }); if (!response.ok) { try { const errorData = await response.json(); if (response.status === 401 && (errorData.error?.includes("revoked") || errorData.error?.includes("Device session revoked"))) { return false; } } catch { if (response.status === 401) { return false; } } } return response.ok; } catch (error) { return true; } } async checkProAccess(component) { const auth = await this.getAuth(); if (!auth) { return false; } if (auth.user.hasLifetimeAccess === true) { return true; } if (auth.user.plan === "pro_lifetime" || auth.user.plan === "lifetime") { return true; } if (component && this.isCriticalComponent(component)) { try { const response = await fetch(`${API_BASE}/api/license/verify`, { method: "POST", headers: { "Authorization": `Bearer ${auth.accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ component, plan: auth.user.plan }) }); if (!response.ok) { return false; } const data = await response.json(); if (data.user?.hasLifetimeAccess === true) { return true; } return data.hasAccess && auth.user.plan !== "free"; } catch { } } if (auth.user.hasProAccess === true) { return true; } return auth.user.plan !== "free"; } isCriticalComponent(component) { const criticalComponents = [ "data-table", "advanced-chart", "dashboard", "kanban", "calendar" ]; return criticalComponents.includes(component); } async getUserInfo() { const auth = await this.getAuth(); return auth?.user || null; } async makeAuthenticatedRequest(url, options = {}) { const auth = await this.requireAuth(); const deviceId = this.getDeviceFingerprint(); return fetch(url, { ...options, headers: { ...options.headers, "Authorization": `Bearer ${auth.accessToken}`, "X-Device-ID": deviceId, "X-Machine-ID": deviceId } }); } async validateDevice() { try { const auth = await this.getAuth(); if (!auth) { return { valid: false, error: "Not authenticated" }; } const deviceId = this.getDeviceFingerprint(); const deviceInfo = DeviceService.getDeviceInfo(); const response = await fetch(`${API_BASE}/api/device/validate`, { method: "POST", headers: { "Authorization": `Bearer ${auth.accessToken}`, "X-Device-ID": deviceId, "Content-Type": "application/json" }, body: JSON.stringify({ deviceInfo }) }); if (response.ok) { const data = await response.json(); return { valid: data.valid }; } else { const error = await response.json(); return { valid: false, error: error.error || "Device validation failed" }; } } catch (error) { return { valid: false, error: "Failed to validate device" }; } } async registerDevice() { try { const auth = await this.getAuth(); if (!auth) { return { success: false, error: "Not authenticated" }; } const deviceId = this.getDeviceFingerprint(); const deviceInfo = DeviceService.getDeviceInfo(); const response = await fetch(`${API_BASE}/api/device/register`, { method: "POST", headers: { "Authorization": `Bearer ${auth.accessToken}`, "X-Device-ID": deviceId, "Content-Type": "application/json" }, body: JSON.stringify({ deviceId, deviceInfo }) }); if (response.ok) { const data = await response.json(); auth.deviceId = deviceId; auth.deviceValidated = true; await this.saveAuth(auth); return { success: true }; } else { const error = await response.json(); if (error.error === "Device limit exceeded") { console.log(chalk.red("\n\u274C Device limit exceeded")); console.log(chalk.yellow(` You have ${error.currentDevices}/${error.maxDevices} devices registered`)); console.log(chalk.blue(" Manage your devices at: https://moonui.dev/dashboard/devices")); } return { success: false, error: error.error }; } } catch (error) { return { success: false, error: "Failed to register device" }; } } }; export { __require, __toESM, require_package, DeviceService, AuthService };