src/Controller/AdminController.php line 54

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  4. use Symfony\Component\Routing\Annotation\Route;
  5. use Symfony\Component\Process\Exception\ProcessFailedException;
  6. use Symfony\Component\Process\Process;
  7. use Symfony\Component\Yaml\Yaml;
  8. use Symfony\Component\HttpFoundation\Request;
  9. use Symfony\Component\HttpFoundation\Response;
  10. use Symfony\Component\HttpFoundation\JsonResponse;
  11. use Symfony\Component\Serializer\Encoder\JsonEncoder;
  12. use Symfony\Contracts\Cache\ItemInterface;
  13. use Symfony\Contracts\Cache\CacheInterface;
  14. use App\Repository\ProjectRepository;
  15. use App\Repository\RedirectRepository;
  16. use App\Repository\HttpsTypeRepository;
  17. use App\Repository\BackendRepository;
  18. use App\Repository\NginxTemplateRepository;
  19. use App\Entity\Project;
  20. use App\Entity\Redirect;
  21. use Doctrine\ORM\EntityManagerInterface;
  22. use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
  23. use App\Form\Type\RedirectType;
  24. use App\Form\Type\ProjectType;
  25. use App\Entity\Ftp;
  26. class AdminController extends AbstractController
  27. {
  28. private $cache;
  29. private $repository;
  30. private $redirectsRepository;
  31. private $https_repository;
  32. private $backend_repository;
  33. private $nginx_template_repository;
  34. private $manager;
  35. public function __construct(CacheInterface $cache, ProjectRepository $repository, RedirectRepository $redirectsRepository, HttpsTypeRepository $https_repository, BackendRepository $backend_repository, NginxTemplateRepository $nginx_template_repository, EntityManagerInterface $manager)
  36. {
  37. $this->cache = $cache;
  38. $this->repository = $repository;
  39. $this->redirectsRepository = $redirectsRepository;
  40. $this->https_repository = $https_repository;
  41. $this->backend_repository = $backend_repository;
  42. $this->nginx_template_repository = $nginx_template_repository;
  43. $this->manager = $manager;
  44. }
  45. /**
  46. * @Route("/", name="admin")
  47. */
  48. public function index()
  49. {
  50. $soft = $this->cache->get('versions', function (ItemInterface $item) {
  51. $item->expiresAfter(604800);
  52. return $this->getVersions();
  53. });
  54. return $this->render('admin/index.html.twig', [
  55. 'controller_name' => 'AdminController',
  56. 'soft' => $soft
  57. ]);
  58. }
  59. private function getVersions()
  60. {
  61. //skip versions check before multicore support
  62. return [];
  63. $process = Process::fromShellCommandline('sudo /webcrate/versions.py');
  64. $process->run();
  65. if (!$process->isSuccessful()) {
  66. throw new ProcessFailedException($process);
  67. }
  68. $soft_json = $process->getOutput();
  69. $encoder = new JsonEncoder();
  70. $soft = [];
  71. if(!empty($soft_json))
  72. {
  73. $soft = $encoder->decode($soft_json, 'json');
  74. }
  75. return $soft;
  76. }
  77. /**
  78. * @Route("/admin/projects", name="admin-projects")
  79. */
  80. public function projects()
  81. {
  82. $list = $this->repository->getListForTable();
  83. return $this->render('admin/projects.html.twig', [
  84. 'controller_name' => 'AdminController',
  85. 'projects' => $list,
  86. ]);
  87. }
  88. /**
  89. * @Route("/admin/redirects", name="admin-redirects")
  90. */
  91. public function redirects()
  92. {
  93. $list = $this->redirectsRepository->getListForTable();
  94. return $this->render('admin/redirects.html.twig', [
  95. 'controller_name' => 'AdminController',
  96. 'redirects' => $list,
  97. ]);
  98. }
  99. /**
  100. * @Route("/admin/project/add", name="project-add")
  101. */
  102. public function newProject(Request $request)
  103. {
  104. $project = new Project();
  105. $form = $this->createForm(ProjectType::class, $project);
  106. if ($request->isMethod('POST'))
  107. {
  108. $form->handleRequest($request);
  109. if ($form->isSubmitted() && $form->isValid())
  110. {
  111. $project = $form->getData();
  112. $project->setUid($this->repository->getFirstAvailableUid());
  113. $ftps = $project->getFtps();
  114. foreach ( $ftps as $ftp ) {
  115. $ftp->setProject($project);
  116. }
  117. $this->manager->persist($project);
  118. $this->manager->flush();
  119. $this->updateProjectsYaml();
  120. return $this->redirectToRoute('admin-projects');
  121. }
  122. }
  123. return $this->render(
  124. 'admin/project.html.twig',
  125. [
  126. 'form' => $form->createView(),
  127. ]
  128. );
  129. }
  130. /**
  131. * @Route("/admin/project/{uid}", name="admin-project")
  132. */
  133. public function project($uid, Request $request)
  134. {
  135. $project = $this->repository->loadByUid($uid);
  136. $form = $this->createForm(ProjectType::class, $project);
  137. if ($request->isMethod('POST'))
  138. {
  139. $form->handleRequest($request);
  140. if ($form->isSubmitted() && $form->isValid())
  141. {
  142. $project = $form->getData();
  143. $ftps = $project->getFtps();
  144. foreach ( $ftps as $ftp ) {
  145. $ftp->setProject($project);
  146. }
  147. $this->manager->persist($project);
  148. $this->manager->flush();
  149. $this->updateProjectsYaml();
  150. return $this->redirectToRoute('admin-projects');
  151. }
  152. }
  153. return $this->render(
  154. 'admin/project.html.twig',
  155. [
  156. 'form' => $form->createView()
  157. ]
  158. );
  159. }
  160. /**
  161. * @Route("/admin/project/{uid}/delete", name="admin-project-delete")
  162. */
  163. public function projectDelete($uid)
  164. {
  165. $project = $this->repository->loadByUid($uid);
  166. $this->manager->remove($project);
  167. $this->manager->flush();
  168. $list = $this->repository->getListForTable();
  169. $this->updateProjectsYaml();
  170. $response = new JsonResponse();
  171. $response->setData([
  172. 'result' => 'ok',
  173. 'projects' => $list
  174. ]);
  175. return $response;
  176. }
  177. /**
  178. * @Route("/admin/project/{uid}/activate", name="admin-project-activate")
  179. */
  180. public function projectActivate($uid)
  181. {
  182. $project = $this->repository->loadByUid($uid);
  183. $project->setActive(true);
  184. $this->manager->flush();
  185. $list = $this->repository->getListForTable();
  186. $this->updateProjectsYaml();
  187. $response = new JsonResponse();
  188. $response->setData([
  189. 'result' => 'ok',
  190. 'projects' => $list
  191. ]);
  192. return $response;
  193. }
  194. /**
  195. * @Route("/admin/project/{uid}/reload", name="admin-project-reload")
  196. */
  197. public function projectReload($uid)
  198. {
  199. $project = $this->repository->loadByUid($uid);
  200. if ( !$project->isActual() ) {
  201. try {
  202. $name = $project->getName();
  203. $process = Process::fromShellCommandline("sudo /webcrate/reload.py $name");
  204. $process->run();
  205. if (!$process->isSuccessful()) {
  206. throw new ProcessFailedException($process);
  207. }
  208. } catch (IOExceptionInterface $exception) {
  209. $debug['error'] = $exception->getMessage();
  210. }
  211. }
  212. $list = $this->repository->getListForTable();
  213. $response = new JsonResponse();
  214. $response->setData([
  215. 'result' => 'ok',
  216. 'projects' => $list
  217. ]);
  218. return $response;
  219. }
  220. /**
  221. * @Route("/admin/reload-config", name="admin-reload-config")
  222. */
  223. public function projectReloadConfig()
  224. {
  225. try {
  226. $process = Process::fromShellCommandline('sudo /webcrate/reload.py');
  227. $process->run();
  228. if (!$process->isSuccessful()) {
  229. throw new ProcessFailedException($process);
  230. }
  231. } catch (IOExceptionInterface $exception) {
  232. $debug['error'] = $exception->getMessage();
  233. }
  234. $response = new JsonResponse();
  235. $response->setData([
  236. 'result' => 'ok',
  237. ]);
  238. return $response;
  239. }
  240. /**
  241. * @Route("/admin/project/{uid}/deactivate", name="admin-project-deactivate")
  242. */
  243. public function projectDeactivate($uid)
  244. {
  245. $project = $this->repository->loadByUid($uid);
  246. $project->setActive(false);
  247. $this->manager->flush();
  248. $list = $this->repository->getListForTable();
  249. $this->updateProjectsYaml();
  250. $response = new JsonResponse();
  251. $response->setData([
  252. 'result' => 'ok',
  253. 'projects' => $list
  254. ]);
  255. return $response;
  256. }
  257. /**
  258. * @Route("/admin/import-projects", name="import-projects")
  259. */
  260. public function importProjects(Request $request): Response
  261. {
  262. $file = $request->files->get('file');
  263. $filepath = $file['tmp_name'];
  264. $projects = Yaml::parseFile($filepath);
  265. foreach ( $projects as $projectname => $project_obj ) {
  266. $project_obj = (object)$project_obj;
  267. $entity = $this->repository->loadByUid($project_obj->uid);
  268. if ( empty($entity) ) {
  269. $project = new Project();
  270. $project_obj->active = !empty($project_obj->active) ? $project_obj->active : false;
  271. $project_obj->memcached = !empty($project_obj->memcached) ? $project_obj->memcached : false;
  272. $project_obj->redis = !empty($project_obj->redis) ? $project_obj->redis : false;
  273. $project_obj->solr = !empty($project_obj->solr) ? $project_obj->solr : false;
  274. $project_obj->elastic = !empty($project_obj->elastic) ? $project_obj->elastic : false;
  275. $project_obj->redirect = !empty($project_obj->redirect) ? $project_obj->redirect : false;
  276. $project_obj->backup = !empty($project_obj->backup) ? $project_obj->backup : false;
  277. $project_obj->gzip = !empty($project_obj->gzip) ? $project_obj->gzip : false;
  278. $project_obj->mysql_db = !empty($project_obj->mysql_db) ? $project_obj->mysql_db : false;
  279. $project_obj->mysql5_db = !empty($project_obj->mysql5_db) ? $project_obj->mysql5_db : false;
  280. $project_obj->postgresql_db = !empty($project_obj->postgresql_db) ? $project_obj->postgresql_db : false;
  281. $project_obj->root_folder = !empty($project_obj->root_folder) ? $project_obj->root_folder : 'data';
  282. $project_obj->password = !empty($project_obj->password) ? $project_obj->password : 'empty_password';
  283. $project_obj->https = !empty($project_obj->https) ? $project_obj->https : 'disabled';
  284. $project_obj->backend = !empty($project_obj->backend) ? $project_obj->backend : 'php';
  285. $project_obj->backend_version = !empty($project_obj->backend_version) ? $project_obj->backend_version : '81';
  286. $project_obj->gunicorn_app_module = !empty($project_obj->gunicorn_app_module) ? $project_obj->gunicorn_app_module : '';
  287. $project_obj->nginx_template = !empty($project_obj->nginx_template) ? $project_obj->nginx_template : 'default';
  288. $project_obj->volume = !empty($project_obj->volume) ? $project_obj->volume : 0;
  289. $project_obj->nginx_block = !empty($project_obj->nginx_block) ? $project_obj->nginx_block : '';
  290. $project_obj->domains = !empty($project_obj->domains) ? $project_obj->domains : [$projectname . '.test'];
  291. $project_obj->nginx_options = !empty($project_obj->nginx_options) ? $project_obj->nginx_options : [];
  292. $project_obj->auth_locations = !empty($project_obj->auth_locations) ? $project_obj->auth_locations : [];
  293. $project_obj->duplicity_filters = !empty($project_obj->duplicity_filters) ? $project_obj->duplicity_filters : [];
  294. $project_obj->ftps = !empty($project_obj->ftps) ? $project_obj->ftps : [];
  295. $project_obj->ip_address = !empty($project_obj->ip_address) ? $project_obj->ip_address : '';
  296. $project->setUid($project_obj->uid);
  297. $project->setName($projectname);
  298. $project->setVolume($project_obj->volume);
  299. $project->setActive($project_obj->active == 'yes' || $project_obj->active === true);
  300. $project->setMemcached($project_obj->memcached == 'yes' || $project_obj->memcached === true);
  301. $project->setRedis($project_obj->redis == 'yes' || $project_obj->redis === true);
  302. $project->setSolr($project_obj->solr == 'yes' || $project_obj->solr === true);
  303. $project->setElastic($project_obj->elastic == 'yes' || $project_obj->elastic === true);
  304. $project->setBackup($project_obj->backup == 'yes' || $project_obj->backup === true);
  305. $project->setRedirect($project_obj->redirect == 'yes' || $project_obj->redirect === true);
  306. $project->setGzip($project_obj->gzip == 'yes' || $project_obj->gzip === true);
  307. $project->setMysql($project_obj->mysql_db == 'yes' || $project_obj->mysql_db === true);
  308. $project->setMysql5($project_obj->mysql5_db == 'yes' || $project_obj->mysql5_db === true);
  309. $project->setPostgre($project_obj->postgresql_db == 'yes' || $project_obj->postgresql_db === true);
  310. $project->setRootFolder($project_obj->root_folder);
  311. $project->setPasswordHash($project_obj->password);
  312. $project->setIpAddress($project_obj->ip_address);
  313. $https = $this->https_repository->findByName($project_obj->https);
  314. $project->setHttps($https);
  315. $backend_version = empty($project_obj->backend_version) ? ( $project_obj->backend == 'php' ? '81' : 'latest' ) : $project_obj->backend_version;
  316. $backend = $this->backend_repository->findByNameAndVersion($project_obj->backend, (string)$backend_version);
  317. $project->setBackend($backend);
  318. if ( !empty($project_obj->gunicorn_app_module) && ( $project_obj->backend == 'gunicorn' ) ) {
  319. $project->setGunicornAppModule($project_obj->gunicorn_app_module);
  320. }
  321. $template = $this->nginx_template_repository->findByName($project_obj->nginx_template);
  322. $project->setNginxTemplate($template);
  323. $project->setNginxBlock($project_obj->nginx_block);
  324. $project->setDomains($project_obj->domains);
  325. $options_array = [];
  326. foreach ( $project_obj->nginx_options as $name => $value ) {
  327. $options_array[] = [ 'name' => $name, 'value' => $value ];
  328. }
  329. $project->setNginxOptions($options_array);
  330. $locations_array = [];
  331. foreach ( $project_obj->auth_locations as $location ) {
  332. $locations_array[] = [
  333. 'path' => $location['path'],
  334. 'title' => $location['title'],
  335. 'user' => $location['user'],
  336. 'password' => $location['password']
  337. ];
  338. }
  339. $project->setAuthLocations($locations_array, true);
  340. $duplicity_filters_array = [];
  341. foreach ( $project_obj->duplicity_filters as $filter ) {
  342. $duplicity_filters_array[] = [
  343. 'mode' => $filter['mode'],
  344. 'path' => $filter['path']
  345. ];
  346. }
  347. $project->setDuplicityFilters($duplicity_filters_array, true);
  348. foreach ( $project_obj->ftps as $index => $ftp_data ) {
  349. $ftp = new Ftp();
  350. $ftp->setName($ftp_data['name']);
  351. $ftp->setWeight($index);
  352. $ftp->setPasswordHash($ftp_data['password']);
  353. $ftp->setHome($ftp_data['home']);
  354. $project->addFtp($ftp);
  355. }
  356. $this->manager->persist($project);
  357. }
  358. }
  359. $this->manager->flush();
  360. $this->updateProjectsYaml();
  361. $list = $this->repository->getListForTable();
  362. $response = new JsonResponse();
  363. $response->setData([
  364. 'result' => 'ok',
  365. 'projects' => $list
  366. ]);
  367. return $response;
  368. }
  369. public function updateProjectsYaml()
  370. {
  371. $ymlData = $this->getYmlData();
  372. try {
  373. $new_file_path = "/webcrate/updated-projects.yml";
  374. file_put_contents($new_file_path, $ymlData);
  375. $process = Process::fromShellCommandline('sudo /webcrate/updateprojects.py');
  376. $process->run();
  377. if (!$process->isSuccessful()) {
  378. throw new ProcessFailedException($process);
  379. }
  380. } catch (IOExceptionInterface $exception) {
  381. $debug['error'] = $exception->getMessage();
  382. }
  383. }
  384. public function getYmlData()
  385. {
  386. $projects = $this->repository->getList();
  387. $projects_list = (object)[];
  388. foreach ( $projects as $project ) {
  389. $projectname = $project->getName();
  390. $projects_list->$projectname = $project->toObject();
  391. }
  392. $ymlData = Yaml::dump($projects_list, 3, 2, Yaml::DUMP_OBJECT_AS_MAP);
  393. return $ymlData;
  394. }
  395. /**
  396. * @Route("/admin/redirect/add", name="redirect-add")
  397. */
  398. public function newRedirect(Request $request)
  399. {
  400. $redirect = new Redirect();
  401. $form = $this->createForm(RedirectType::class, $redirect);
  402. if ($request->isMethod('POST'))
  403. {
  404. $form->handleRequest($request);
  405. if ($form->isSubmitted() && $form->isValid())
  406. {
  407. $redirect = $form->getData();
  408. $this->manager->persist($redirect);
  409. $this->manager->flush();
  410. $this->updateRedirectsYaml();
  411. return $this->redirectToRoute('admin-redirects');
  412. }
  413. }
  414. return $this->render(
  415. 'admin/redirect.html.twig',
  416. [
  417. 'form' => $form->createView()
  418. ]
  419. );
  420. }
  421. /**
  422. * @Route("/admin/redirect/{name}", name="admin-redirect")
  423. */
  424. public function adminRedirect($name, Request $request)
  425. {
  426. $redirect = $this->redirectsRepository->loadByName($name);
  427. $form = $this->createForm(RedirectType::class, $redirect);
  428. if ($request->isMethod('POST'))
  429. {
  430. $form->handleRequest($request);
  431. if ($form->isSubmitted() && $form->isValid())
  432. {
  433. $redirect = $form->getData();
  434. $this->manager->persist($redirect);
  435. $this->manager->flush();
  436. $this->updateRedirectsYaml();
  437. return $this->redirectToRoute('admin-redirects');
  438. }
  439. }
  440. return $this->render(
  441. 'admin/redirect.html.twig',
  442. [
  443. 'form' => $form->createView()
  444. ]
  445. );
  446. }
  447. /**
  448. * @Route("/admin/redirect/{name}/delete", name="admin-redirect-delete")
  449. */
  450. public function redirectDelete($name)
  451. {
  452. $redirect = $this->redirectsRepository->loadByName($name);
  453. $this->manager->remove($redirect);
  454. $this->manager->flush();
  455. $list = $this->redirectsRepository->getListForTable();
  456. $this->updateRedirectsYaml();
  457. $response = new JsonResponse();
  458. $response->setData([
  459. 'result' => 'ok',
  460. 'redirects' => $list
  461. ]);
  462. return $response;
  463. }
  464. /**
  465. * @Route("/admin/redirect/{name}/activate", name="admin-redirect-activate")
  466. */
  467. public function redirectActivate($name)
  468. {
  469. $redirect = $this->redirectsRepository->loadByName($name);
  470. $redirect->setActive(true);
  471. $this->manager->flush();
  472. $list = $this->redirectsRepository->getListForTable();
  473. $this->updateRedirectsYaml();
  474. $response = new JsonResponse();
  475. $response->setData([
  476. 'result' => 'ok',
  477. 'redirects' => $list
  478. ]);
  479. return $response;
  480. }
  481. /**
  482. * @Route("/admin/redirect/{name}/reload", name="admin-redirect-reload")
  483. */
  484. public function redirectReload($name)
  485. {
  486. $redirect = $this->redirectsRepository->loadByName($name);
  487. if ( !$redirect->isActual() ) {
  488. try {
  489. $name = $redirect->getName();
  490. $process = Process::fromShellCommandline("sudo /webcrate/reload-redirect.py $name");
  491. $process->run();
  492. if (!$process->isSuccessful()) {
  493. throw new ProcessFailedException($process);
  494. }
  495. } catch (IOExceptionInterface $exception) {
  496. $debug['error'] = $exception->getMessage();
  497. }
  498. }
  499. $list = $this->redirectsRepository->getListForTable();
  500. $response = new JsonResponse();
  501. $response->setData([
  502. 'result' => 'ok',
  503. 'redirects' => $list
  504. ]);
  505. return $response;
  506. }
  507. /**
  508. * @Route("/admin/reload-redirect-config", name="admin-reload-redirect-config")
  509. */
  510. public function redirectReloadConfig()
  511. {
  512. try {
  513. $process = Process::fromShellCommandline('sudo /webcrate/reload-redirect.py');
  514. $process->run();
  515. if (!$process->isSuccessful()) {
  516. throw new ProcessFailedException($process);
  517. }
  518. } catch (IOExceptionInterface $exception) {
  519. $debug['error'] = $exception->getMessage();
  520. }
  521. $response = new JsonResponse();
  522. $response->setData([
  523. 'result' => 'ok'
  524. ]);
  525. return $response;
  526. }
  527. /**
  528. * @Route("/admin/redirect/{name}/deactivate", name="admin-redirect-deactivate")
  529. */
  530. public function redirectDeactivate($name)
  531. {
  532. $redirect = $this->redirectsRepository->loadByName($name);
  533. $redirect->setActive(false);
  534. $this->manager->flush();
  535. $list = $this->redirectsRepository->getListForTable();
  536. $this->updateRedirectsYaml();
  537. $response = new JsonResponse();
  538. $response->setData([
  539. 'result' => 'ok',
  540. 'redirects' => $list
  541. ]);
  542. return $response;
  543. }
  544. /**
  545. * @Route("/admin/import-redirects", name="import-redirects")
  546. */
  547. public function importRedirects(Request $request): Response
  548. {
  549. $file = $request->files->get('file');
  550. $filepath = $file['tmp_name'];
  551. $redirects = Yaml::parseFile($filepath);
  552. foreach ( $redirects as $redirectname => $redirect_obj ) {
  553. $redirect_obj = (object)$redirect_obj;
  554. $entity = $this->redirectsRepository->loadByName($redirectname);
  555. if ( empty($entity) ) {
  556. $redirect = new Redirect();
  557. $redirect_obj->active = !empty($redirect_obj->active) ? $redirect_obj->active : false;
  558. $redirect_obj->https = !empty($redirect_obj->https) ? $redirect_obj->https : 'disabled';
  559. $redirect_obj->domains = !empty($redirect_obj->domains) ? $redirect_obj->domains : [$redirectname . '.test'];
  560. $redirect->setActive($redirect_obj->active == 'yes' || $redirect_obj->active === true);
  561. $redirect->setName($redirectname);
  562. $redirect->setUrl($redirect_obj->url);
  563. $https = $this->https_repository->findByName($redirect_obj->https);
  564. $redirect->setHttps($https);
  565. $redirect->setDomains($redirect_obj->domains);
  566. $this->manager->persist($redirect);
  567. }
  568. }
  569. $this->manager->flush();
  570. $this->updateRedirectsYaml();
  571. $list = $this->redirectsRepository->getListForTable();
  572. $response = new JsonResponse();
  573. $response->setData([
  574. 'result' => 'ok',
  575. 'redirects' => $list
  576. ]);
  577. return $response;
  578. }
  579. public function updateRedirectsYaml()
  580. {
  581. $ymlData = $this->getRedirectsYmlData();
  582. try {
  583. $new_file_path = "/webcrate/updated-redirects.yml";
  584. file_put_contents($new_file_path, $ymlData);
  585. $process = Process::fromShellCommandline('sudo /webcrate/updateredirects.py');
  586. $process->run();
  587. if (!$process->isSuccessful()) {
  588. throw new ProcessFailedException($process);
  589. }
  590. } catch (IOExceptionInterface $exception) {
  591. $debug['error'] = $exception->getMessage();
  592. }
  593. }
  594. public function getRedirectsYmlData()
  595. {
  596. $redirects = $this->redirectsRepository->getList();
  597. $redirects_list = (object)[];
  598. foreach ( $redirects as $redirect ) {
  599. $redirectname = $redirect->getName();
  600. $redirects_list->$redirectname = $redirect->toObject();
  601. }
  602. $ymlData = Yaml::dump($redirects_list, 3, 2, Yaml::DUMP_OBJECT_AS_MAP);
  603. return $ymlData;
  604. }
  605. }