Sindbad~EG File Manager
<?php
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\LockableTrait;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(
name: 'vhosts:generate',
description: 'Generate the OpenLitespeed vhosts from the apache configuration, and configure Openresty vhosts in the Redis database.',
aliases: ['openresty:generate'],
hidden: false
)]
class VhostsGenerate extends BaseCommand
{
use LockableTrait;
const RET_RELOAD_NOT_NEEDED = 0;
const RET_RELOAD_NEEDED = 1;
protected function configure(): void
{
$this
->addOption('force', 'f',InputOption::VALUE_OPTIONAL, 'Force the rebuild of Openresty vhosts ?', true)
->addOption('domain', 'd',InputOption::VALUE_OPTIONAL, 'Just update one domain name, filter the rest', null)
->setHelp("This command will create the OpenLitespeed vhosts and then, create the openresty equivalent of vhosts by filling the redis DB with data.
It will parse the cPanel userdata and the httpd.conf, extract the useful information and fill up the
Redis database with it. OpenResty will check this Redis DB to retrieve the proxyPass and SSL
information for each requests and then put it in his own internal cache.");
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$start_time = microtime(true);
$time_start = $start_time;
$domain = $input->getOption('domain');
if(!empty($domain)){
$output->writeln("Just one domain will be updated $domain");
}
if (is_null($domain) && !$this->lock()) {
$output->writeln('The command is already running in another process.');
return 0;
}
if($input->hasOption('force')){
$force = $input->getOption('force') === true || $input->getOption('force') == 'true';
} else {
$force = true;
}
if($this->checkGenerateNeeded()){
$retVal = self::RET_RELOAD_NEEDED;
} else {
$retVal = self::RET_RELOAD_NOT_NEEDED;
if(!$force){
$output->writeln("Generation not needed, httpd.conf hasn't changed recently.");
$output->writeln("To force the generation, you can add : --force true");
return $retVal;
}
}
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After checkGenerateNeeded', (microtime(true) - $time_start)/60));
// Parse cPanel user data to get the map of subdomain => real domain
if(!$this->cpanelUserdataParser->parse()){
$this->writeErrorAndDie($output, "Error while parsing the cPanel userdata", BaseCommand::RETURN_PARSE_ERR);
}
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After >cpanelUserdataParser->parse', (microtime(true) - $time_start)/60));
$cpUserdata = $this->cpanelUserdataParser->getData();
if ($output->isVerbose()) {
$output->writeln("cPanel userdata parsed !");
}
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After >cpanelUserdataParser->getData', (microtime(true) - $time_start)/60));
if($domain !== null){
$newUserData = [];
foreach($cpUserdata as $user => $domainList){
foreach($domainList as $subDomain => $realDomain){
if($subDomain === $domain || $realDomain === $domain){
$newUserData[$user] = [$subDomain => $domain];
break 2;
}
}
}
if(empty($newUserData)){
$output->writeln("The domain $domain was not found");
return Command::FAILURE;
}
$cpUserdata = $newUserData;
}
// Parse the httpd.conf
$this->apacheHttpdParser->setDataTransformer([
$this->serverSelector,
$this->enrichDt->setCpUserdata($cpUserdata)
]); // TODO : SET $cpUserdata TO DT
if(!$this->apacheHttpdParser->parse($this->apacheHttpdConf, $domain)){
$this->writeErrorAndDie($output, "Error while parsing the httpd.conf", BaseCommand::RETURN_PARSE_ERR);
}
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After apacheHttpdParser', (microtime(true) - $time_start)/60));
$data = $this->apacheHttpdParser->getData();
if($domain !== null && empty($data)){
$output->writeln("The domain $domain was not found in the httpd.conf");
return Command::FAILURE;
}
if ($output->isVerbose()) {
$output->writeln(count($data) . " keys before data expansion");
}
// Duplicate / Add some additional data on redis but under new Key
// Fix the aliases problem
$data = $this->expandDt->transform($data);
if ($output->isVerbose()) {
$output->writeln(count($data) . " keys after data expansion");
}
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After expand', (microtime(true) - $time_start)/60));
if($data === false || !is_array($data) || empty($data)){
$this->writeErrorAndDie($output, "Error while parsing the httpd.conf, unexpected return", BaseCommand::RETURN_PARSE_ERR);
}
if ($output->isVerbose()) {
$output->writeln('httpd.conf parsed !');
}
if(!$this->disableOpenLitespeed){
$output->writeln('Generete OpenLitespeed Config.');
try {
$this->lswsConfigManager->initialize(true);
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After lswsConfigManager->initialize(', (microtime(true) - $time_start)/60));
$this->lswsListenerManager->load();
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After lswsConfigManager->load(', (microtime(true) - $time_start)/60));
$this->lswsListenerManager->setListener('Http', ['address' => '*:8088', 'secure' => 0]);
$this->lswsListenerManager->setListener('Https', [
'address' => '*:4438',
'secure' => 1,
'keyFile' => '/var/cpanel/ssl/cpanel/mycpanel.pem',
'certFile' => '/var/cpanel/ssl/cpanel/mycpanel.pem'
]);
$this->lswsListenerManager->setListener('Apache Http', ['address' => '*:8089', 'secure' => 0]);
$this->lswsListenerManager->setListener('Apache Https', [
'address' => '*:4439',
'secure' => 1,
'keyFile' => '/var/cpanel/ssl/cpanel/mycpanel.pem',
'certFile' => '/var/cpanel/ssl/cpanel/mycpanel.pem'
]);
$this->lswsListenerManager->persist();
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After lswsConfigManager->persist(', (microtime(true) - $time_start)/60));
} catch (\Exception $e) {
$output->writeln("There was errors when trying to generate OpenLitespeed config\n" . $e->getMessage());
}
$output->writeln("Generate OpenLitespeed Vhosts.");
foreach($data as $vhost){
$this->createLswsVhost($vhost, 'default');
$this->createLswsVhost($vhost, 'apache');
};
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After vhost loop', (microtime(true) - $time_start)/60));
try {
$this->lswsVhostManager->persistAll();
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After lswsVhostManager->persistAll', (microtime(true) - $time_start)/60));
$this->lswsCommandRunner->restart();
} catch (\Exception $e) {
$output->writeLn(sprintf('%d %s %s', __LINE__, 'After exception', (microtime(true) - $time_start)/60));
$output->writeln("There was errors when trying to generate OpenLitespeed config\n" . $e->getMessage());
}
}
$end_time = microtime(true);
$execution_time = ($end_time - $start_time);
$start_time = microtime(true);
$output->writeln('Time: ' . $execution_time);
if($output->isVerbose()){
$this->openrestyProcessor->setOutput($output);
$this->openrestyProcessor->setVerbosity(true);
}
if(!$this->openrestyProcessor->generate($data, $domain)){
$this->writeErrorAndDie($output, "Error while inserting the data inside Redis", BaseCommand::RETURN_CREATE_ERR);
}
if(!empty($domain)){
$r = $this->openrestyApiClient->purgeDomain($domain);
} else {
$r = $this->openrestyApiClient->purge();
}
$end_time = microtime(true);
$execution_time = ($end_time - $start_time);
$start_time = microtime(true);
$output->writeln('Time: ' . $execution_time);
$output->writeln($r === true ? "Openresty cache purged" : "Error while purging openresty cache");
$output->writeln("Data successfully inserted inside the OpenResty Redis DB !");
return $retVal;
}
private function createLswsVhost(array $vhost, string $template = 'default')
{
$required = ['serverName', 'cpUser', 'ip', 'docRoot', 'rawServerAlias'];
if(count(array_intersect_key(array_flip($required), $vhost)) !== count($required)){
return;
}
$domain = $vhost['serverName'];
$user = $group = $vhost['cpUser'];
$serverIp = $vhost['ip'];
$root = $vhost['docRoot'];
$serverAlias = str_replace(' ', ', ', trim($vhost['rawServerAlias']));
$serverAdmin = 'webmaster@' . $domain;
$vhostName = $domain . '_' . $template;
switch ($template) {
case 'default' :
$listeners = ['Http', 'Https'];
break;
case 'apache' :
$listeners = ['Apache Http', 'Apache Https'];
break;
default :
$listeners = [];
}
$this->lswsVhostManager->setRule($vhostName, 'o2template', $template);
$this->lswsVhostManager->setRule($vhostName, 'o2listeners', $listeners);
$this->lswsVhostManager->setRule($vhostName, 'o2serverIp', $serverIp);
$this->lswsVhostManager->setRule($vhostName, 'vhRoot', "conf/vhosts/$domain");
$this->lswsVhostManager->setRule($vhostName, 'configFile', "conf/vhosts/$domain/" . $template . '_vhconf.conf');
$this->lswsVhostManager->setRule($vhostName, 'allowSymbolLink', 1);
$this->lswsVhostManager->setRule($vhostName, 'enableScript', 1);
$this->lswsVhostManager->setRule($vhostName, 'restrained', 1);
$this->lswsVhostManager->setRule($vhostName, 'setUIDMode', 2);
$this->lswsVhostManager->setRule($vhostName, 'user', $user);
$this->lswsVhostManager->setRule($vhostName, 'group', $group);
$this->lswsVhostManager->setRule($vhostName, 'docRoot', $root);
$this->lswsVhostManager->setRule($vhostName, 'vhDomain', $domain);
$this->lswsVhostManager->setRule($vhostName, 'vhAliases', $serverAlias);
$this->lswsVhostManager->setRule($vhostName, 'adminEmails', $serverAdmin);
$this->lswsVhostManager->setRule($vhostName, 'enableGzip', '0');
$this->lswsVhostManager->setRule($vhostName, 'enableBr', '0');
$this->lswsVhostManager->setRule($vhostName, 'enableIpGeo', '0');
$this->lswsVhostManager->setRule($vhostName, 'index', [
'useServer' => '0',
'indexFiles' => 'index.php, index.php8, index.php7, index.php5, index.perl, index.pl, index.plx, index.ppl, index.cgi, index.jsp, index.jp, index.phtml, index.shtml, index.xhtml, index.html, index.htm, index.js'
]);
$this->lswsVhostManager->setRule($vhostName, 'vhssl', [
'keyFile' => "/var/cpanel/ssl/apache_tls/$domain/combined",
'certFile' => "/var/cpanel/ssl/apache_tls/$domain/combined"
]);
$this->lswsVhostManager->setRule($vhostName, 'module cache', [
'storagePath' => '/speed/lscache/$VH_NAME',
]);
}
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists