DeployDevController.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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 -p $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 -p /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 -p /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. if ($GLOBALS['debug'] == true) { error_log("Running Certbot for Domain " . $domain); }
  84. 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);
  85. if ($returnCode !== 0) {
  86. error_log("deploy: ERROR: certbot failed to create certificate on dev.$domain, details => " . implode("\n", $output));
  87. http_response_code(500);
  88. echo json_encode(['error' => 'Certbot failed', 'details' => implode("\n", $output)]);
  89. return;
  90. }
  91. if ($GLOBALS['debug'] == true) { error_log("Replace sslsettings in in Apache Config Template"); }
  92. $configContent = str_replace('DOMAINNAME', $domain, $configContent);
  93. if ($GLOBALS['debug'] == true) { error_log("Writing apache config file to " . $configFile); }
  94. if (file_put_contents($configFile, $configContent) != true) {
  95. error_log("deploy: ERROR: while writing apache config");
  96. echo json_encode(['error' => 'Failed to write Apache config', 'details' => []]);
  97. }
  98. exec('sudo /usr/bin/systemctl reload apache2 2>&1', $apacheOutput, $apacheReturnCode);
  99. if ($GLOBALS['debug'] == true) { error_log("Restarting Apache"); }
  100. if ($apacheReturnCode !== 0) {
  101. error_log("deploy: ERROR: Apache Reload error, details => " . implode("\n", $apacheOutput));
  102. http_response_code(500);
  103. echo json_encode(['error' => 'Failed to reload Apache', 'details' => implode("\n", $apacheOutput)]);
  104. return;
  105. }
  106. // Create PHP-FPM User
  107. // /etc/php/8.2/fpm/user.in
  108. if ($userExisted != true) {
  109. if ($GLOBALS['debug'] == true) {error_log("Writing PHP-FPM Config for : " . $username); }
  110. $phpContent = file_get_contents("/etc/php/8.2/fpm/user.in");
  111. $phpContent = str_replace("USERNAME", $username, $phpContent);
  112. file_put_contents("/etc/php/8.2/fpm/pool.d/" . $username . ".conf", $phpContent);
  113. if ($GLOBALS['debug'] == true) {error_log("Restarting PHP-FPM : " . $username);}
  114. exec('sudo /usr/bin/systemctl reload php8.2-fpm', $phpOutput, $phpReturnCode);
  115. if ($phpReturnCode !== 0) {
  116. error_log("deploy: ERROR: PHP-FPM reload error, details => " . implode("\n", $phpOutput));
  117. http_response_code(500);
  118. echo json_encode(['error' => 'Failed to reload PHP-FPM', 'details' => implode("\n", $phpOutput)]);
  119. return;
  120. }
  121. }
  122. /*
  123. //Create MySQL Database
  124. if ($GLOBALS['debug'] == true) { error_log("creating database: " . $databaseName); }
  125. $sqlCommand = "sudo mysql -e \"create database $databaseName;\" 2>&1";
  126. exec($sqlCommand,$mysqlOutput,$mysqlReturnCode);
  127. if ($mysqlReturnCode !== 0) {
  128. error_log("deploy: MySQL create database error, details => " . implode("\n", $mysqlOutput));
  129. http_response_code(500);
  130. echo json_encode(['error' => 'Failed to create database $databaseName', 'details' => implode("\n", $mysqlOutput)]);
  131. return;
  132. }
  133. //Create MySQL User
  134. $sqlCommand = "sudo mysql -e \"create user '" . $username . "'@'localhost' identified by '" . $username . "'\" 2>&1";
  135. if ($GLOBALS['debug'] == true) { error_log("creating database user: " . $sqlCommand); }
  136. exec($sqlCommand,$mysqlOutput,$mysqlReturnCode);
  137. if ($mysqlReturnCode !== 0) {
  138. error_log("deploy: MySQL create user $username error, details => " . implode("\n", $mysqlOutput));
  139. http_response_code(500);
  140. echo json_encode(['error' => 'Failed to create databaseuser $username', 'details' => implode("\n", $mysqlOutput)]);
  141. return;
  142. }
  143. //Grant permission
  144. if ($GLOBALS['debug'] == true) { error_log("granting permission on $databaseName to: " . $username); }
  145. $sqlCommand = "sudo mysql -e \"grant all on " . $databaseName . ".* to '" . $username . "'@'localhost';\" 2>&1";
  146. exec($sqlCommand,$mysqlOutput,$mysqlReturnCode);
  147. if ($mysqlReturnCode !== 0) {
  148. error_log("deploy: MySQL grant permission to user $username error, details => " . implode("\n", $mysqlOutput));
  149. http_response_code(500);
  150. echo json_encode(['error' => 'Failed to grant permission on $databaseName ot $username', 'details' => implode("\n", $mysqlOutput)]);
  151. return;
  152. }
  153. // Deployment of the Concrete CMS Application
  154. if ($GLOBALS['debug'] == true) { error_log("Copy master directory to $webDir"); }
  155. exec("sudo /usr/bin/cp -r /var/www/master/* $webDir 2>&1",$cpOutput,$cpReturnCode);
  156. if ($cpReturnCode !== 0) {
  157. error_log("deploy: Error while copying master installation to $webDir, details => " . implode("\n", $cpOutput));
  158. http_response_code(500);
  159. echo json_encode(['error' => 'Failed to copy master installation into web dir', 'details' => implode("\n", $cpOutput)]);
  160. return;
  161. }
  162. if ($GLOBALS['debug'] == true) { error_log("Chown WebDir $webDir"); }
  163. exec("sudo /usr/bin/chown $username:$username $webDir -R 2>&1", $chownOutput, $chownReturnCode);
  164. if ($chownReturnCode !== 0) {
  165. error_log("deploy: chown error on $webDir, details => " . implode("\n", $chownOutput));
  166. http_response_code(500);
  167. echo json_encode(['error' => 'Failed to chown web dir', 'details' => implode("\n", $chownOutput)]);
  168. return;
  169. }
  170. if ($GLOBALS['debug'] == true) { error_log("Writing install script to: /home/" . $username . "/" . $databaseName ."-inst.sh"); }
  171. $installCommand = "#!/bin/bash\n";
  172. $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";
  173. $installCommand .= "$webDir/concrete/bin/concrete5 c5:package-install studio_templates 2>&1 \n";
  174. $installCommand .= "$webDir/concrete/bin/concrete5 studio:setup 2>&1 \n";
  175. file_put_contents("/var/www/scripts/" . $databaseName ."-inst.sh",$installCommand);
  176. if ($GLOBALS['debug'] == true) { error_log("Chmod install script: /home/" . $username . "/" . $databaseName ." -inst.sh"); }
  177. exec("sudo /usr/bin/chmod a+x /var/www/scripts/" . $databaseName ."-inst.sh 2>&1", $chmodOutput, $chmodReturnCode);
  178. if ($chmodReturnCode !== 0) {
  179. error_log("deploy: ERROR: chmod on /var/www/scripts/" . $databaseName ."-inst.sh failed, details => " . implode("\n", $chmodOutput));
  180. http_response_code(500);
  181. echo json_encode(['error' => 'Failed to chmod on /var/www/scripts/' . $databaseName .'-inst.sh', 'details' => implode("\n", $chmodOutput)]);
  182. return;
  183. }
  184. if ($GLOBALS['debug'] == true) { error_log("Executing Concrete 5 installation: " . $installCommand); }
  185. exec("sudo -u $username /var/www/sudo-concrete5.sh /var/www/scripts/" . $databaseName ."-inst.sh",$createOutput, $createReturnCode);
  186. if ($createReturnCode !== 0) {
  187. error_log("deploy: cms installation error, details => " . implode("\n", $createOutput));
  188. http_response_code(500);
  189. echo json_encode(['error' => 'Failed to install cms system', 'details' => implode("\n", $createOutput)]);
  190. return;
  191. }
  192. */
  193. echo json_encode(['success' => 'Development site deployed successfully','details' => '']);
  194. }
  195. //exec("php /path/to/scripts/post_install_setup.php", $output, $code);
  196. public static function revert($data): void {
  197. $username = $data['username'] ?? '';
  198. $domain = $data['domain'] ?? '';
  199. $adminName = $data['admin_name'] ?? '';
  200. $adminPassword = $data['admin_password'] ?? '';
  201. $webDir = "/home/$username/dev.$domain";
  202. $databaseName = $username . "_dev_";
  203. $databaseName = $databaseName . str_replace(['-', '.'], ["_", "_"], $domain);
  204. exec("sudo /usr/bin/rm -rf $webDir 2>&1", $rmOutput, $rmReturnCode);
  205. if ($rmReturnCode !== 0) {
  206. error_log("revert: error on rm $webDir -r, details => " . implode("\n", $rmOutput));
  207. http_response_code(500);
  208. echo json_encode(['error' => 'Failed to remove webdir dir', 'details' => implode("\n", $rmOutput)]);
  209. return;
  210. }
  211. exec("sudo /usr/bin/mkdir $webDir 2>&1", $mkdirOutput, $mkdirReturnCode);
  212. error_log("revert: error on mkdir $webDir, details => " . implode("\n", $mkdirOutput));
  213. if ($mkdirReturnCode !== 0) {
  214. http_response_code(500);
  215. echo json_encode(['error' => 'Failed to create web dir', 'details' => implode("\n", $mkdirOutput)]);
  216. return;
  217. }
  218. exec("sudo /usr/bin/chown $username:$username $webDir 2>&1", $chownOutput, $chownReturnCode);
  219. if ($chownReturnCode !== 0) {
  220. error_log("revert: chown error on $webDir, details => " . implode("\n", $chownOutput));
  221. http_response_code(500);
  222. echo json_encode(['error' => 'Failed to chown web dir', 'details' => implode("\n", $chownOutput)]);
  223. return;
  224. }
  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. */
  272. echo json_encode(['success' => 'Development site deployed successfully']);
  273. }
  274. public static function terminate($data): void {
  275. $username = $data['username'] ?? '';
  276. $domain = $data['domain'] ?? '';
  277. if (empty($username)) {
  278. error_log("terminate: ERROR: No username provided");
  279. http_response_code(400);
  280. error_log(print_r($data,true));
  281. echo json_encode(['error' => 'Missing username']);
  282. return;
  283. }
  284. if( strpos(file_get_contents("/etc/passwd"),$username) == false) {
  285. error_log("terminate: ERROR: User $username does not exist");
  286. http_response_code(400);
  287. error_log(print_r($data,true));
  288. echo json_encode(['error' => 'Unknown user']);
  289. return;
  290. }
  291. // remove PHP-FPM User
  292. if ($GLOBALS['debug'] == true) {error_log("Removing PHP-FPM Config for : " . $username); }
  293. $configName = "/etc/php/8.2/fpm/pool.d/" . $username . ".conf";
  294. exec("sudo /usr/bin/rm -f $configName 2>&1", $userOutput, $userReturnCode);
  295. if ($GLOBALS['debug'] == true) {error_log("Restarting PHP-FPM : " . $username);}
  296. exec('sudo /usr/bin/systemctl reload php8.2-fpm', $phpOutput, $phpReturnCode);
  297. if ($phpReturnCode !== 0) {
  298. error_log("deploy: ERROR: PHP-FPM reload error, details => " . implode("\n", $phpOutput));
  299. http_response_code(500);
  300. echo json_encode(['error' => 'Failed to reload PHP-FPM', 'details' => implode("\n", $phpOutput)]);
  301. return;
  302. }
  303. if(!empty($domain)) {
  304. $configFile = "/etc/apache2/sites-enabled/dev.$domain.conf";
  305. if ($GLOBALS['debug'] == true) { error_log("Remove config of user : " . $username); }
  306. exec("sudo /usr/bin/rm -f $configFile 2>&1", $userOutput, $userReturnCode);
  307. exec("sudo /usr/bin/certbot delete --cert-name dev.$domain --non-interactive 2>&1", $output, $returnCode);
  308. if ($returnCode !== 0) {
  309. error_log("deploy: ERROR: certbot failed to delete certificate on dev.$domain, details => " . implode("\n", $output));
  310. http_response_code(500);
  311. echo json_encode(['error' => 'Certbot failed', 'details' => implode("\n", $output)]);
  312. return;
  313. }
  314. exec('sudo /usr/bin/systemctl reload apache2 2>&1', $apacheOutput, $apacheReturnCode);
  315. if ($GLOBALS['debug'] == true) { error_log("Restarting Apache"); }
  316. if ($apacheReturnCode !== 0) {
  317. error_log("deploy: ERROR: Apache Reload error, details => " . implode("\n", $apacheOutput));
  318. http_response_code(500);
  319. echo json_encode(['error' => 'Failed to reload Apache', 'details' => implode("\n", $apacheOutput)]);
  320. return;
  321. }
  322. }
  323. // Remove user and files
  324. if ($GLOBALS['debug'] == true) {error_log("Remove User: " . $username); }
  325. exec("sudo /usr/sbin/userdel -r -f $username 2>&1", $userOutput, $userReturnCode);
  326. if ($userReturnCode !== 0) {
  327. error_log("deploy: ERROR: Userdel for $username failed, details => " . implode("\n", $userOutput));
  328. http_response_code(500);
  329. echo json_encode(['error' => 'Failed to remove user', 'details' => implode("\n", $userOutput)]);
  330. return;
  331. }
  332. echo json_encode(['success' => 'Removing user ' . $username . ' successfully']);
  333. }
  334. }