123 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import * as plugins from './mod.plugins.js';
 | 
						|
import * as net from 'net';
 | 
						|
import { logger } from '../gitzone.logging.js';
 | 
						|
 | 
						|
/**
 | 
						|
 * Check if a port is available
 | 
						|
 */
 | 
						|
export const isPortAvailable = async (port: number): Promise<boolean> => {
 | 
						|
  return new Promise((resolve) => {
 | 
						|
    const server = net.createServer();
 | 
						|
    
 | 
						|
    server.once('error', () => {
 | 
						|
      resolve(false);
 | 
						|
    });
 | 
						|
    
 | 
						|
    server.once('listening', () => {
 | 
						|
      server.close();
 | 
						|
      resolve(true);
 | 
						|
    });
 | 
						|
    
 | 
						|
    server.listen(port, '0.0.0.0');
 | 
						|
  });
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Get a random available port between 20000 and 30000
 | 
						|
 */
 | 
						|
export const getRandomAvailablePort = async (): Promise<number> => {
 | 
						|
  const maxAttempts = 100;
 | 
						|
  
 | 
						|
  for (let i = 0; i < maxAttempts; i++) {
 | 
						|
    const port = Math.floor(Math.random() * 10001) + 20000;
 | 
						|
    if (await isPortAvailable(port)) {
 | 
						|
      return port;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  // Fallback: let the system assign a port
 | 
						|
  return 0;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Get the project name from package.json or directory
 | 
						|
 */
 | 
						|
export const getProjectName = (): string => {
 | 
						|
  try {
 | 
						|
    const packageJsonPath = plugins.path.join(process.cwd(), 'package.json');
 | 
						|
    if (plugins.smartfile.fs.fileExistsSync(packageJsonPath)) {
 | 
						|
      const packageJson = plugins.smartfile.fs.toObjectSync(packageJsonPath);
 | 
						|
      if (packageJson.name) {
 | 
						|
        // Sanitize: @fin.cx/skr → fin-cx-skr
 | 
						|
        return packageJson.name.replace(/@/g, '').replace(/[\/\.]/g, '-');
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } catch (error) {
 | 
						|
    // Ignore errors and fall back to directory name
 | 
						|
  }
 | 
						|
  
 | 
						|
  return plugins.path.basename(process.cwd());
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Print a header with decorative lines
 | 
						|
 */
 | 
						|
export const printHeader = (title: string) => {
 | 
						|
  console.log();
 | 
						|
  logger.log('info', '═══════════════════════════════════════════════════════════════');
 | 
						|
  logger.log('info', `  ${title}`);
 | 
						|
  logger.log('info', '═══════════════════════════════════════════════════════════════');
 | 
						|
  console.log();
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Format bytes to human readable string
 | 
						|
 */
 | 
						|
export const formatBytes = (bytes: number): string => {
 | 
						|
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
 | 
						|
  let size = bytes;
 | 
						|
  let unitIndex = 0;
 | 
						|
  
 | 
						|
  while (size >= 1024 && unitIndex < units.length - 1) {
 | 
						|
    size /= 1024;
 | 
						|
    unitIndex++;
 | 
						|
  }
 | 
						|
  
 | 
						|
  return `${size.toFixed(2)} ${units[unitIndex]}`;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Get the local network IP address
 | 
						|
 */
 | 
						|
export const getLocalNetworkIp = async (): Promise<string> => {
 | 
						|
  const smartnetworkInstance = new plugins.smartnetwork.SmartNetwork();
 | 
						|
  const gateways = await smartnetworkInstance.getGateways();
 | 
						|
  
 | 
						|
  // Find the best local IP from network interfaces
 | 
						|
  for (const interfaceName of Object.keys(gateways)) {
 | 
						|
    const interfaces = gateways[interfaceName];
 | 
						|
    for (const iface of interfaces) {
 | 
						|
      // Skip loopback and internal interfaces
 | 
						|
      if (!iface.internal && iface.family === 'IPv4') {
 | 
						|
        const address = iface.address;
 | 
						|
        // Prefer LAN IPs
 | 
						|
        if (address.startsWith('192.168.') || address.startsWith('10.') || address.startsWith('172.')) {
 | 
						|
          return address;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  // Fallback: try to get any non-internal IPv4
 | 
						|
  for (const interfaceName of Object.keys(gateways)) {
 | 
						|
    const interfaces = gateways[interfaceName];
 | 
						|
    for (const iface of interfaces) {
 | 
						|
      if (!iface.internal && iface.family === 'IPv4') {
 | 
						|
        return iface.address;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  // Last resort: localhost
 | 
						|
  return 'localhost';
 | 
						|
}; |