DeployDevController.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <?php
  2. namespace application\controllers;
  3. class DeployDevController {
  4. public static function deploy($data): void {
  5. $username = $data['username'];
  6. $domain = $data['domain'];
  7. $adminName = $data['admin_name'] ?? '';
  8. $adminPassword = $data['admin_password'];
  9. $databaseName = $username . "_dev_";
  10. $databaseName = $databaseName . str_replace(['-', '.'], ["_", "_"], $domain);
  11. $webDir = "/home/$username/dev.$domain";
  12. $configTemplate = '/etc/apache2/site-config.in';
  13. $configFile = "/etc/apache2/sites-enabled/dev.$domain.conf";
  14. if (empty($username) || empty($domain) || empty($adminName) || empty($adminPassword)) {
  15. error_log("deploy: ERROR: No username, domain, admin_name or admin_password provided");
  16. http_response_code(400);
  17. error_log("deploy: ERROR: UserName 1 " . $username);
  18. error_log("deploy: ERROR: Domain 1 " . $domain);
  19. error_log("deploy: ERROR: UserName 2 " . $data['username'] );
  20. error_log("deploy: ERROR: Domain 2 " . $data['domain'] );
  21. error_log("deploy: ERROR: AdminName " . $adminName);
  22. error_log("deploy: ERROR: AdminPasswd" . $adminPassword);
  23. error_log(print_r($data,true));
  24. echo json_encode(['error' => 'Missing required parameters']);
  25. return;
  26. }
  27. error_log(" Starting function deploy for " . $username . " and " . $domain . " DebugMode: " . $GLOBALS['debug']);
  28. $userExisted = false;
  29. if( strpos(file_get_contents("/etc/passwd"),$username) !== false) {
  30. $userExisted = true;
  31. }
  32. if ($userExisted != true) {
  33. // Create user without login access
  34. if ($GLOBALS['debug'] == true) {error_log("Adding User: " . $username); }
  35. exec("sudo /usr/sbin/useradd -m -k -M -s /usr/sbin/nologin $username 2>&1", $userOutput, $userReturnCode);
  36. if ($userReturnCode !== 0) {
  37. error_log("deploy: ERROR: Useradd for $username failed, details => " . implode("\n", $userOutput));
  38. http_response_code(500);
  39. echo json_encode(['error' => 'Failed to create user', 'details' => implode("\n", $userOutput)]);
  40. return;
  41. }
  42. }
  43. if ($GLOBALS['debug'] == true) { error_log("Creating Webdir ($webDir) for : " . $username); }
  44. exec("sudo /usr/bin/mkdir $webDir 2>&1", $mkdirOutput, $mkdirReturnCode);
  45. if ($mkdirReturnCode !== 0) {
  46. error_log("deploy: ERROR: Create Webdir for $username failed, details => ". implode("\n", $mkdirOutput));
  47. http_response_code(500);
  48. echo json_encode(['error' => 'Failed to create web dir', 'details' => implode("\n", $mkdirOutput)]);
  49. return;
  50. }
  51. if ($GLOBALS['debug'] == true) { error_log("Creating logdir for : " . $username); }
  52. if (is_dir("/home/$username/logs") != true) {
  53. exec("sudo /usr/bin/mkdir /home/$username/logs 2>&1", $mkdirOutput, $mkdirReturnCode);
  54. if ($mkdirReturnCode !== 0) {
  55. error_log("deploy: ERROR: Failed to create log directory for $username failed, details => " . implode("\n", $mkdirOutput));
  56. http_response_code(500);
  57. echo json_encode(['error' => 'Failed to create logs dir', 'details' => implode("\n", $mkdirOutput)]);
  58. return;
  59. }
  60. }
  61. if ($GLOBALS['debug'] == true) { error_log("Creating backup dir for : " . $username); }
  62. if (is_dir("/home/$username/backups") != true) {
  63. exec("sudo /usr/bin/mkdir /home/$username/backups 2>&1", $mkdirOutput, $mkdirReturnCode);
  64. if ($mkdirReturnCode !== 0) {
  65. error_log("deploy: ERROR: mkdir /home/$username/backups failed, details => " . implode("\n", $mkdirOutput));
  66. http_response_code(500);
  67. echo json_encode(['error' => 'Failed to create backups dir', 'details' => implode("\n", $mkdirOutput)]);
  68. return;
  69. }
  70. }
  71. if ($GLOBALS['debug'] == true) { error_log("Chown homedir: " . $username); }
  72. exec("sudo /usr/bin/chown $username:$username /home/$username -R 2>&1", $chownOutput, $chownReturnCode);
  73. if ($chownReturnCode !== 0) {
  74. error_log("deploy: ERROR: chown on /home/$username failed, details => " . implode("\n", $chownOutput));
  75. http_response_code(500);
  76. echo json_encode(['error' => 'Failed to chown backups dir', 'details' => implode("\n", $chownOutput)]);
  77. return;
  78. }
  79. if ($GLOBALS['debug'] == true) { error_log("Reading Apache Config Template /etc/apache2/site-config.in"); }
  80. $configContent = file_get_contents($configTemplate);
  81. if ($GLOBALS['debug'] == true) { error_log("Replace config settings in Apache Config Template"); }
  82. $configContent = str_replace(['DOCUMENTROOT', 'SERVERNAME','USERNAME', 'DOMAINNAME', 'SERVERALIAS'], [$webDir, "dev.$domain",$username,"dev.$domain", "" ], $configContent);
  83. /*
  84. if ($GLOBALS['debug'] == true) { error_log("Running Certbot for Domain " . $domain); }
  85. exec("sudo /usr/bin/certbot certonly --webroot -w /etc/apache2/letsencrypt -d dev.$domain --non-interactive --agree-tos --email admin@$domain 2>&1", $output, $returnCode);
  86. if ($returnCode !== 0) {
  87. error_log("deploy: ERROR: certbot failed to create certificate on dev.$domain, details => " . implode("\n", $output));
  88. http_response_code(500);
  89. echo json_encode(['error' => 'Certbot failed', 'details' => implode("\n", $output)]);
  90. return;
  91. }
  92. */
  93. if ($GLOBALS['debug'] == true) { error_log("Replace sslsettings in in Apache Config Template"); }
  94. $certDir = "/etc/letsencrypt/live/dev.$domain";
  95. $configContent = str_replace('DOMAINNAME', $domain, $configContent);
  96. if ($GLOBALS['debug'] == true) { error_log("Writing apache config file to " . $configFile); }
  97. if (file_put_contents($configFile, $configContent) != true) {
  98. error_log("deploy: ERROR: while writing apache config");
  99. echo json_encode(['error' => 'Failed to write Apache config', 'details' => []]);
  100. }
  101. exec('sudo /usr/bin/systemctl reload apache2 2>&1', $apacheOutput, $apacheReturnCode);
  102. if ($GLOBALS['debug'] == true) { error_log("Restarting Apache"); }
  103. if ($apacheReturnCode !== 0) {
  104. error_log("deploy: ERROR: Apache Reload error, details => " . implode("\n", $apacheOutput));
  105. http_response_code(500);
  106. echo json_encode(['error' => 'Failed to reload Apache', 'details' => implode("\n", $apacheOutput)]);
  107. return;
  108. }
  109. // Create PHP-FPM User
  110. // /etc/php/8.2/fpm/user.in
  111. if ($userExisted != true) {
  112. if ($GLOBALS['debug'] == true) {error_log("Writing PHP-FPM Config for : " . $username); }
  113. $phpContent = file_get_contents("/etc/php/8.2/fpm/user.in");
  114. $phpContent = str_replace("USERNAME", $username, $phpContent);
  115. file_put_contents("/etc/php/8.2/fpm/pool.d/" . $username . ".conf", $phpContent);
  116. if ($GLOBALS['debug'] == true) {error_log("Restarting PHP-FPM : " . $username);}
  117. exec('sudo /usr/bin/systemctl reload php8.2-fpm', $phpOutput, $phpReturnCode);
  118. if ($phpReturnCode !== 0) {
  119. error_log("deploy: ERROR: PHP-FPM reload error, details => " . implode("\n", $phpOutput));
  120. http_response_code(500);
  121. echo json_encode(['error' => 'Failed to reload PHP-FPM', 'details' => implode("\n", $phpOutput)]);
  122. return;
  123. }
  124. }
  125. //Create MySQL Database
  126. if ($GLOBALS['debug'] == true) { error_log("creating database: " . $databaseName); }
  127. $sqlCommand = "sudo mysql -e \"create database $databaseName;\" 2>&1";
  128. exec($sqlCommand,$mysqlOutput,$mysqlReturnCode);
  129. if ($mysqlReturnCode !== 0) {
  130. error_log("deploy: MySQL create database error, details => " . implode("\n", $mysqlOutput));
  131. http_response_code(500);
  132. echo json_encode(['error' => 'Failed to create database $databaseName', 'details' => implode("\n", $mysqlOutput)]);
  133. return;
  134. }
  135. //Create MySQL User
  136. $sqlCommand = "sudo mysql -e \"create user '" . $username . "'@'localhost' identified by '" . $username . "'\" 2>&1";
  137. if ($GLOBALS['debug'] == true) { error_log("creating database user: " . $sqlCommand); }
  138. exec($sqlCommand,$mysqlOutput,$mysqlReturnCode);
  139. if ($mysqlReturnCode !== 0) {
  140. error_log("deploy: MySQL create user $username error, details => " . implode("\n", $mysqlOutput));
  141. http_response_code(500);
  142. echo json_encode(['error' => 'Failed to create databaseuser $username', 'details' => implode("\n", $mysqlOutput)]);
  143. return;
  144. }
  145. //Grant permission
  146. if ($GLOBALS['debug'] == true) { error_log("granting permission on $databaseName to: " . $username); }
  147. $sqlCommand = "sudo mysql -e \"grant all on " . $databaseName . ".* to '" . $username . "'@'localhost';\" 2>&1";
  148. exec($sqlCommand,$mysqlOutput,$mysqlReturnCode);
  149. if ($mysqlReturnCode !== 0) {
  150. error_log("deploy: MySQL grant permission to user $username error, details => " . implode("\n", $mysqlOutput));
  151. http_response_code(500);
  152. echo json_encode(['error' => 'Failed to grant permission on $databaseName ot $username', 'details' => implode("\n", $mysqlOutput)]);
  153. return;
  154. }
  155. // Deployment of the Concrete CMS Application
  156. if ($GLOBALS['debug'] == true) { error_log("Copy master directory to $webDir"); }
  157. exec("sudo /usr/bin/cp -r /var/www/master/* $webDir 2>&1",$cpOutput,$cpReturnCode);
  158. if ($cpReturnCode !== 0) {
  159. error_log("deploy: Error while copying master installation to $webDir, details => " . implode("\n", $cpOutput));
  160. http_response_code(500);
  161. echo json_encode(['error' => 'Failed to copy master installation into web dir', 'details' => implode("\n", $cpOutput)]);
  162. return;
  163. }
  164. if ($GLOBALS['debug'] == true) { error_log("Chown WebDir $webDir"); }
  165. exec("sudo /usr/bin/chown $username:$username $webDir -R 2>&1", $chownOutput, $chownReturnCode);
  166. if ($chownReturnCode !== 0) {
  167. error_log("deploy: chown error on $webDir, details => " . implode("\n", $chownOutput));
  168. http_response_code(500);
  169. echo json_encode(['error' => 'Failed to chown web dir', 'details' => implode("\n", $chownOutput)]);
  170. return;
  171. }
  172. if ($GLOBALS['debug'] == true) { error_log("Writing install script to: /home/" . $username . "/" . $databaseName ."-inst.sh"); }
  173. $installCommand = "#!/bin/bash\n";
  174. $installCommand .= "$webDir/concrete/bin/concrete5 c5:install --starting-point=restaurantly --db-server='localhost' --db-username='" . $username . "' --db-password='" . $username ."' --db-database='" . $databaseName . "' --timezone='Europe/Zurich' --admin-email='" . $adminName . "' --admin-password='" . $adminPassword . "' --site-locale=ch_DE --language=ch_DE --site='" . $domain . "' -n 2>&1 \n";
  175. $installCommand .= "$webDir/concrete/bin/concrete5 c5:package-install studio_templates 2>&1 \n";
  176. $installCommand .= "$webDir/concrete/bin/concrete5 studio:setup 2>&1 \n";
  177. file_put_contents("/var/www/scripts/" . $databaseName ."-inst.sh",$installCommand);
  178. if ($GLOBALS['debug'] == true) { error_log("Chmod install script: /home/" . $username . "/" . $databaseName ." -inst.sh"); }
  179. exec("sudo /usr/bin/chmod a+x /var/www/scripts/" . $databaseName ."-inst.sh 2>&1", $chmodOutput, $chmodReturnCode);
  180. if ($chmodReturnCode !== 0) {
  181. error_log("deploy: ERROR: chmod on /var/www/scripts/" . $databaseName ."-inst.sh failed, details => " . implode("\n", $chmodOutput));
  182. http_response_code(500);
  183. echo json_encode(['error' => 'Failed to chmod on /var/www/scripts/' . $databaseName .'-inst.sh', 'details' => implode("\n", $chmodOutput)]);
  184. return;
  185. }
  186. if ($GLOBALS['debug'] == true) { error_log("Executing Concrete 5 installation: " . $installCommand); }
  187. exec("sudo -u $username /var/www/sudo-concrete5.sh /var/www/scripts/" . $databaseName ."-inst.sh",$createOutput, $createReturnCode);
  188. if ($createReturnCode !== 0) {
  189. error_log("deploy: cms installation error, details => " . implode("\n", $createOutput));
  190. http_response_code(500);
  191. echo json_encode(['error' => 'Failed to install cms system', 'details' => implode("\n", $createOutput)]);
  192. return;
  193. }
  194. echo json_encode(['success' => 'Development site deployed successfully','details' => implode("\n", $createOutput)]);
  195. }
  196. //exec("php /path/to/scripts/post_install_setup.php", $output, $code);
  197. public static function revert($data): void {
  198. $username = $data['username'] ?? '';
  199. $domain = $data['domain'] ?? '';
  200. $adminName = $data['admin_name'] ?? '';
  201. $adminPassword = $data['admin_password'] ?? '';
  202. $webDir = "/home/$username/dev.$domain";
  203. $databaseName = $username . "_dev_";
  204. $databaseName = $databaseName . str_replace(['-', '.'], ["_", "_"], $domain);
  205. exec("sudo /usr/bin/rm -rf $webDir 2>&1", $rmOutput, $rmReturnCode);
  206. if ($rmReturnCode !== 0) {
  207. error_log("revert: error on rm $webDir -r, details => " . implode("\n", $rmOutput));
  208. http_response_code(500);
  209. echo json_encode(['error' => 'Failed to remove webdir dir', 'details' => implode("\n", $rmOutput)]);
  210. return;
  211. }
  212. exec("sudo /usr/bin/mkdir $webDir 2>&1", $mkdirOutput, $mkdirReturnCode);
  213. error_log("revert: error on mkdir $webDir, details => " . implode("\n", $mkdirOutput));
  214. if ($mkdirReturnCode !== 0) {
  215. http_response_code(500);
  216. echo json_encode(['error' => 'Failed to create web dir', 'details' => implode("\n", $mkdirOutput)]);
  217. return;
  218. }
  219. exec("sudo /usr/bin/chown $username:$username $webDir 2>&1", $chownOutput, $chownReturnCode);
  220. if ($chownReturnCode !== 0) {
  221. error_log("revert: chown error on $webDir, details => " . implode("\n", $chownOutput));
  222. http_response_code(500);
  223. echo json_encode(['error' => 'Failed to chown web dir', 'details' => implode("\n", $chownOutput)]);
  224. return;
  225. }
  226. $sqlCommand = "sudo mysql \"drop database $databaseName;\" 2>&1";
  227. exec($sqlCommand,$mysqlOutput,$mysqlReturnCode);
  228. if ($mysqlReturnCode !== 0) {
  229. error_log("revert: MySQL drop database error, details => " . implode("\n", $mysqlOutput));
  230. http_response_code(500);
  231. echo json_encode(['error' => 'Failed to drop database $usernmae', 'details' => implode("\n", $mysqlOutput)]);
  232. return;
  233. }
  234. $sqlCommand = "sudo mysql \"create database $databaseName;\" 2>&1";
  235. exec($sqlCommand,$mysqlOutput,$mysqlReturnCode);
  236. if ($mysqlReturnCode !== 0) {
  237. error_log("revert: MySQL create database error, details => " . implode("\n", $mysqlOutput));
  238. http_response_code(500);
  239. echo json_encode(['error' => 'Failed to create database $usernmae', 'details' => implode("\n", $mysqlOutput)]);
  240. return;
  241. }
  242. // Deployment of the Concrete CMS Application
  243. exec("sudo /usr/bin/cp -r /var/www/master/* $webDir 2>&1",$cpOutput,$cpReturnCode);
  244. if ($cpReturnCode !== 0) {
  245. error_log("revert: Error while copying master installation to $webDir, details => " . implode("\n", $cpOutput));
  246. http_response_code(500);
  247. echo json_encode(['error' => 'Failed to copy master installation into web dir', 'details' => implode("\n", $cpOutput)]);
  248. return;
  249. }
  250. exec("sudo /usr/bin/chown $username:$username $webDir -R 2>&1", $chownOutput, $chownReturnCode);
  251. if ($chownReturnCode !== 0) {
  252. error_log("revert: chown error on $webDir, details => " . implode("\n", $chownOutput));
  253. http_response_code(500);
  254. echo json_encode(['error' => 'Failed to chown web dir', 'details' => implode("\n", $chownOutput)]);
  255. return;
  256. }
  257. exec("sudo -u $username /var/www/sudo-concrete5.sh $webDir/concrete/bin/concrete5 c5:install --db-server=localhost --db-username=$username --db-password=$username --db-database=$databaseName --timezone=\"Europe/Zurich\" --admin-email=$adminName --admin-password=$adminPassword --site-locale=ch_DE --language=ch_DE --site=$domain --starting-point=atomik_blank -n -vv 2>&1",$createOutput, $createReturnCode);
  258. if ($createReturnCode !== 0) {
  259. error_log("revert: cms installation error, details => " . implode("\n", $createOutput));
  260. http_response_code(500);
  261. echo json_encode(['error' => 'Failed to install cms system', 'details' => implode("\n", $createOutput)]);
  262. return;
  263. }
  264. exec("sudo /usr/bin/chown $username:$username $webDir 2>&1", $chownOutput, $chownReturnCode);
  265. if ($chownReturnCode !== 0) {
  266. error_log("revert: chown error on $webDir, details => " . implode("\n", $chownOutput));
  267. http_response_code(500);
  268. echo json_encode(['error' => 'Failed to chown web dir', 'details' => implode("\n", $chownOutput)]);
  269. return;
  270. }
  271. echo json_encode(['success' => 'Development site deployed successfully']);
  272. }
  273. public static function terminate($data): void {
  274. }
  275. }