Skip to content

Commit cadc568

Browse files
committed
WebApp Installer: impl. a generic installer class structure and a app (wordpress) definition class
1 parent 9831e51 commit cadc568

File tree

3 files changed

+212
-6
lines changed

3 files changed

+212
-6
lines changed

web/add/webapp/index.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
}
3232

3333
$v_web_apps = [
34-
[ 'name'=>'Wordpress', 'group'=>'cms','version'=>'1.2.3', 'thumbnail'=>'/images/webapps/wp-thumb.png' ],
34+
[ 'name'=>'Wordpress', 'group'=>'cms','version'=>'5.2.2', 'thumbnail'=>'/images/webapps/wp-thumb.png' ],
3535
[ 'name'=>'Drupal', 'group'=>'cms', 'version'=>'1.2.3', 'thumbnail'=>'/images/webapps/drupal-thumb.png' ],
3636
[ 'name'=>'Joomla', 'group'=>'cms', 'version'=>'1.2.3', 'thumbnail'=>'/images/webapps/joomla-thumb.png' ],
3737

@@ -41,9 +41,22 @@
4141

4242
[ 'name'=>'Laravel', 'group'=>'starter', 'version'=>'1.2.3', 'thumbnail'=>'/images/webapps/laravel-thumb.png' ],
4343
[ 'name'=>'Symfony', 'group'=>'starter', 'version'=>'1.2.3', 'thumbnail'=>'/images/webapps/symfony-thumb.png' ],
44-
4544
];
4645

46+
// Check GET request
47+
if (!empty($_GET['app'])) {
48+
require 'installer.php';
49+
try {
50+
$hestia = new HestiaApp();
51+
$installer = new AppInstaller($_GET['app'], $v_domain, $hestia);
52+
} catch (Exception $e) {
53+
$_SESSION['error_msg'] = $e->getMessage();
54+
header('Location: /add/webapp/?domain=' . $v_domain);
55+
exit();
56+
}
57+
$GLOBALS['WebappInstaller'] = $installer;
58+
}
59+
4760
// Check POST request
4861
if (!empty($_POST['ok'])) {
4962

@@ -52,10 +65,21 @@
5265
header('location: /login/');
5366
exit();
5467
}
68+
69+
if ($installer) {
70+
if (!$installer->execute($_POST)){
71+
$result = $installer->getStatus();
72+
$_SESSION['error_msg'] = implode(PHP_EOL, $result);
73+
}
74+
}
75+
}
76+
77+
if($installer) {
78+
render_page($user, $TAB, 'setup_webapp');
79+
} else {
80+
render_page($user, $TAB, 'add_webapp');
5581
}
5682

57-
// Render page
58-
render_page($user, $TAB, 'add_webapp');
5983

6084
// Flush session messages
6185
unset($_SESSION['error_msg']);

web/add/webapp/installer.php

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<?php
2+
3+
abstract class BaseSetup {
4+
public function getConfig($section=null) {
5+
return (!empty($section))? $this->config[$section] : $this->config;
6+
}
7+
8+
public function getOptions() {
9+
return $this->getConfig('form');
10+
}
11+
12+
public function withDatabase() {
13+
return ($this->getConfig('database') === true);
14+
}
15+
}
16+
17+
18+
class WordpressSetup extends BaseSetup {
19+
protected $config = [
20+
'form' => [
21+
'protocol' => [
22+
'type' => 'select',
23+
'options' => ['http','ion','https'],
24+
'value' => 'ion',
25+
],
26+
'subdir' => ['type'=>'text', 'value'=>'/'],
27+
'site_name' => ['type'=>'text', 'value'=>'Wordpress Blog'],
28+
'site_description' => ['value'=>'Another wordpresss site'],
29+
'wordpress_account_username' => ['value'=>'wpadmin'],
30+
'wordpress_account_password' => 'password',
31+
],
32+
'database' => true,
33+
'url' => 'https://wordpress.org/wordpress-5.2.2.tar.gz'
34+
];
35+
}
36+
37+
38+
class HestiaApp {
39+
40+
public function run($cmd, $args, &$return_code=null) {
41+
42+
$cli_script = HESTIA_CMD . '/' . basename($cmd);
43+
$cli_arguments = '';
44+
45+
if (!empty($args) && is_array($args)) {
46+
foreach ($args as $arg) {
47+
$cli_arguments .= escapeshellarg($arg) . ' ';
48+
}
49+
} else {
50+
$cli_arguments = escapeshellarg($args);
51+
}
52+
53+
exec ($cli_script . ' ' . $cli_arguments, $output, $return_code);
54+
55+
$result['code'] = $return_code;
56+
$result['raw'] = $output;
57+
$result['text'] = implode( PHP_EOL, $result['raw']);
58+
$result['json'] = json_decode($result['text'], true);
59+
60+
return (object)$result;
61+
}
62+
63+
public function realuser() {
64+
// Logged in user
65+
return $_SESSION['user'];
66+
}
67+
68+
public function user() {
69+
// Effective user
70+
if ($this->realuser() == 'admin' && !empty($_SESSION['look'])) {
71+
return $_SESSION['look'];
72+
}
73+
return $this->realuser();
74+
}
75+
76+
public function userOwnsDomain($domain) {
77+
$status = null;
78+
$this->run('v-list-web-domain', [$this->user(), $domain, 'json'], $status);
79+
return ($status === 0);
80+
}
81+
82+
public function databaseAdd($dbname,$dbuser,$dbpass) {
83+
$v_password = tempnam("/tmp","vst");
84+
$fp = fopen($v_password, "w");
85+
fwrite($fp, $dbpass."\n");
86+
fclose($fp);
87+
$this->run('v-add-database', [$this->user(), $dbname, $dbuser, $v_password], $status);
88+
unlink($v_password);
89+
return ($status === 0);
90+
}
91+
}
92+
93+
94+
class AppInstaller {
95+
96+
private $domain;
97+
private $appsetup;
98+
private $appcontext;
99+
private $formNamespace = 'webapp';
100+
private $errors;
101+
102+
private $database_config = [
103+
'database_name' => 'text',
104+
'database_user' => 'text',
105+
'database_password' => 'password',
106+
];
107+
108+
public function __construct($app, $domain, $context) {
109+
$this->domain = $domain;
110+
$this->appcontext = $context;
111+
112+
if (!$this->appcontext->userOwnsDomain($domain)) {
113+
throw new Exception("User does not have access to domain [$domain]");
114+
}
115+
116+
$appclass = ucfirst($app).'Setup';
117+
if (class_exists($appclass)) {
118+
$this->appsetup = new $appclass();
119+
}
120+
121+
if (!$this->appsetup) {
122+
throw new Exception( "Application [".ucfirst($app)."] does not have a installer" );
123+
}
124+
}
125+
126+
public function getStatus() {
127+
return $this->errors;
128+
}
129+
130+
public function formNs() {
131+
return $this->formNamespace;
132+
}
133+
134+
public function getOptions() {
135+
if(!$this->appsetup) return;
136+
137+
$options = $this->appsetup->getOptions();
138+
if ($this->appsetup->withDatabase()) {
139+
$options = array_merge($options, $this->database_config);
140+
}
141+
return $options;
142+
}
143+
144+
public function filterOptions($options)
145+
{
146+
$filteredoptions = [];
147+
array_walk($options, function($value, $key) use(&$filteredoptions) {
148+
if (strpos($key, $this->formNs().'_')===0) {
149+
$option = str_replace($this->formNs().'_','',$key);
150+
$filteredoptions[$option] = $value;
151+
}
152+
});
153+
return $filteredoptions;
154+
}
155+
156+
public function execute($options) {
157+
if (!$this->appsetup) return;
158+
159+
$options = $this->filterOptions($options);
160+
161+
$random_num = random_int(10000, 99999);
162+
if ($this->appsetup->withDatabase()) {
163+
164+
if(empty($options['database_name'])) {
165+
$options['database_name'] = $random_num;
166+
}
167+
168+
if(empty($options['database_user'])) {
169+
$options['database_user'] = $random_num;
170+
}
171+
172+
if(empty($options['database_password'])) {
173+
$options['database_password'] = bin2hex(random_bytes(10));
174+
}
175+
176+
if(!$this->appcontext->databaseAdd($options['database_name'], $options['database_user'], $options['database_password'])){
177+
$this->errors[] = "Error adding database";
178+
return false;
179+
}
180+
}
181+
}
182+
}

web/templates/admin/add_webapp.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div class="l-center edit">
22
<div class="l-sort clearfix">
33
<div class="l-unit-toolbar__buttonstrip">
4-
<a class="ui-button cancel" id="btn-back" href="/list/web/"><i class="fas fa-arrow-left status-icon blue"></i> <?=__('Back')?></a>
4+
<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>
55
</div>
66
<div class="l-unit-toolbar__buttonstrip float-right">
77
<?php
@@ -37,7 +37,7 @@
3737
<div class="card-details">
3838
<p class="card-title"><?=$webapp['name']?></p>
3939
<p>version: <?=$webapp['version']?></p>
40-
<button class="ui-buttdon cancel">Setup</button>
40+
<a href="/add/webapp/?app=<?=$webapp['name']?>&domain=<?=$v_domain?>" class="ui-button cancel">Setup</a>
4141
</div>
4242
</div>
4343
<?php endforeach; ?>

0 commit comments

Comments
 (0)