Просмотр исходного кода

WebApp Installer: impl. a generic installer class structure and a app (wordpress) definition class

Robert Zollner 6 лет назад
Родитель
Сommit
cadc5684a7
3 измененных файлов с 212 добавлено и 6 удалено
  1. 28 4
      web/add/webapp/index.php
  2. 182 0
      web/add/webapp/installer.php
  3. 2 2
      web/templates/admin/add_webapp.html

+ 28 - 4
web/add/webapp/index.php

@@ -31,7 +31,7 @@ if(!in_array($v_domain, $user_domains)) {
 }
 
 $v_web_apps = [
-    [ 'name'=>'Wordpress', 'group'=>'cms','version'=>'1.2.3', 'thumbnail'=>'/images/webapps/wp-thumb.png' ],
+    [ 'name'=>'Wordpress', 'group'=>'cms','version'=>'5.2.2', 'thumbnail'=>'/images/webapps/wp-thumb.png' ],
     [ 'name'=>'Drupal', 'group'=>'cms', 'version'=>'1.2.3', 'thumbnail'=>'/images/webapps/drupal-thumb.png' ],
     [ 'name'=>'Joomla', 'group'=>'cms', 'version'=>'1.2.3', 'thumbnail'=>'/images/webapps/joomla-thumb.png' ],
 
@@ -41,9 +41,22 @@ $v_web_apps = [
 
     [ 'name'=>'Laravel', 'group'=>'starter', 'version'=>'1.2.3', 'thumbnail'=>'/images/webapps/laravel-thumb.png' ],
     [ 'name'=>'Symfony', 'group'=>'starter', 'version'=>'1.2.3', 'thumbnail'=>'/images/webapps/symfony-thumb.png' ],
-
 ];
 
+// Check GET request
+if (!empty($_GET['app'])) {
+    require 'installer.php';
+    try {
+        $hestia = new HestiaApp();
+        $installer = new AppInstaller($_GET['app'], $v_domain, $hestia);
+    } catch (Exception $e) {
+        $_SESSION['error_msg'] = $e->getMessage();
+        header('Location: /add/webapp/?domain=' . $v_domain);
+        exit();
+    }
+    $GLOBALS['WebappInstaller'] = $installer;
+}
+
 // Check POST request
 if (!empty($_POST['ok'])) {
 
@@ -52,10 +65,21 @@ if (!empty($_POST['ok'])) {
         header('location: /login/');
         exit();
     }
+
+    if ($installer) {
+        if (!$installer->execute($_POST)){
+            $result = $installer->getStatus();
+            $_SESSION['error_msg'] = implode(PHP_EOL, $result);
+        }
+    }
+}
+
+if($installer) {
+    render_page($user, $TAB, 'setup_webapp');
+} else {
+    render_page($user, $TAB, 'add_webapp');
 }
 
-// Render page
-render_page($user, $TAB, 'add_webapp');
 
 // Flush session messages
 unset($_SESSION['error_msg']);

+ 182 - 0
web/add/webapp/installer.php

@@ -0,0 +1,182 @@
+<?php
+
+abstract class BaseSetup {
+    public function getConfig($section=null) {
+        return (!empty($section))? $this->config[$section] : $this->config;
+    }
+
+    public function getOptions() {
+        return $this->getConfig('form');
+    }
+
+    public function withDatabase() {
+        return ($this->getConfig('database') === true);
+    }
+}
+
+
+class WordpressSetup extends BaseSetup {
+    protected $config = [
+        'form' => [
+            'protocol' => [ 
+                'type' => 'select',
+                'options' => ['http','ion','https'],
+                'value' => 'ion',
+            ],
+            'subdir' => ['type'=>'text', 'value'=>'/'],
+            'site_name' => ['type'=>'text', 'value'=>'Wordpress Blog'],
+            'site_description' => ['value'=>'Another wordpresss site'],
+            'wordpress_account_username' => ['value'=>'wpadmin'],
+            'wordpress_account_password' => 'password',
+            ],
+        'database' => true,
+        'url' => 'https://wordpress.org/wordpress-5.2.2.tar.gz'
+    ];
+}
+
+
+class HestiaApp {
+
+    public function run($cmd, $args, &$return_code=null) {
+
+        $cli_script = HESTIA_CMD . '/' . basename($cmd);
+        $cli_arguments = '';
+
+        if (!empty($args) && is_array($args)) {
+            foreach ($args as $arg) {
+                $cli_arguments .= escapeshellarg($arg) . ' ';
+            }
+        } else {
+            $cli_arguments = escapeshellarg($args);
+        }
+
+        exec ($cli_script . ' ' . $cli_arguments, $output, $return_code);
+
+        $result['code'] = $return_code;
+        $result['raw']  = $output;
+        $result['text'] = implode( PHP_EOL, $result['raw']);
+        $result['json'] = json_decode($result['text'], true);
+
+        return (object)$result;
+    }
+
+    public function realuser() {
+        // Logged in user 
+        return $_SESSION['user'];
+    }
+
+    public function user() {
+        // Effective user
+        if ($this->realuser() == 'admin' && !empty($_SESSION['look'])) {
+            return $_SESSION['look'];
+        }
+        return $this->realuser();
+    }
+
+    public function userOwnsDomain($domain) {
+        $status = null;
+        $this->run('v-list-web-domain', [$this->user(), $domain, 'json'], $status);
+        return ($status === 0);
+    }
+
+    public function databaseAdd($dbname,$dbuser,$dbpass) {
+        $v_password = tempnam("/tmp","vst");
+        $fp = fopen($v_password, "w");
+        fwrite($fp, $dbpass."\n");
+        fclose($fp);
+        $this->run('v-add-database', [$this->user(), $dbname, $dbuser, $v_password], $status);
+        unlink($v_password);
+        return ($status === 0);
+    }
+}
+
+
+class AppInstaller {
+
+    private $domain;
+    private $appsetup;
+    private $appcontext;
+    private $formNamespace = 'webapp';
+    private $errors;
+
+    private $database_config = [
+        'database_name' => 'text',
+        'database_user' => 'text',
+        'database_password' => 'password',
+    ];
+
+    public function __construct($app, $domain, $context) {
+        $this->domain = $domain;
+        $this->appcontext = $context;
+
+        if (!$this->appcontext->userOwnsDomain($domain)) {
+            throw new Exception("User does not have access to domain [$domain]");
+        }
+
+        $appclass = ucfirst($app).'Setup';
+        if (class_exists($appclass)) {
+            $this->appsetup = new $appclass();
+        }
+
+        if (!$this->appsetup) {
+            throw new Exception( "Application [".ucfirst($app)."] does not have a installer" );
+        }
+    }
+
+    public function getStatus() {
+        return $this->errors;
+    }
+
+    public function formNs() {
+        return $this->formNamespace;
+    }
+
+    public function getOptions() {
+        if(!$this->appsetup) return;
+
+        $options = $this->appsetup->getOptions();
+        if ($this->appsetup->withDatabase()) {
+            $options = array_merge($options, $this->database_config);
+        }
+        return $options;
+    }
+
+    public function filterOptions($options)
+    {
+        $filteredoptions = [];
+        array_walk($options, function($value, $key) use(&$filteredoptions) {
+            if (strpos($key, $this->formNs().'_')===0) {
+                $option = str_replace($this->formNs().'_','',$key);
+                $filteredoptions[$option] = $value;
+            }
+        });
+        return $filteredoptions;
+    }
+
+    public function execute($options) {
+        if (!$this->appsetup) return;
+
+        $options = $this->filterOptions($options);
+
+        $random_num = random_int(10000, 99999);
+        if ($this->appsetup->withDatabase()) {
+
+            if(empty($options['database_name'])) {
+                $options['database_name'] = $random_num;
+            }
+
+            if(empty($options['database_user'])) {
+                $options['database_user'] = $random_num;
+            }
+
+            if(empty($options['database_password'])) {
+                $options['database_password'] = bin2hex(random_bytes(10));
+            }
+
+            if(!$this->appcontext->databaseAdd($options['database_name'], $options['database_user'], $options['database_password'])){
+                $this->errors[] = "Error adding database";
+                return false;
+            }
+        }
+    }
+}

+ 2 - 2
web/templates/admin/add_webapp.html

@@ -1,7 +1,7 @@
 <div class="l-center edit">
         <div class="l-sort clearfix">
           <div class="l-unit-toolbar__buttonstrip">
-            <a class="ui-button cancel" id="btn-back" href="/list/web/"><i class="fas fa-arrow-left status-icon blue"></i> <?=__('Back')?></a>
+            <a class="ui-button cancel" id="btn-back" href="/edit/web/?domain=<?=$v_domain?>"><i class="fas fa-arrow-left status-icon blue"></i> <?=__('Back')?></a>
           </div>
           <div class="l-unit-toolbar__buttonstrip float-right">
               <?php
@@ -37,7 +37,7 @@
                     <div class="card-details">
                         <p class="card-title"><?=$webapp['name']?></p>
                         <p>version: <?=$webapp['version']?></p>
-                        <button class="ui-buttdon cancel">Setup</button>
+                        <a href="/add/webapp/?app=<?=$webapp['name']?>&domain=<?=$v_domain?>" class="ui-button cancel">Setup</a>
                     </div>
                 </div>
             <?php endforeach; ?>